我是靠谱客的博主 甜蜜刺猬,最近开发中收集的这篇文章主要介绍Java多线程及相关知识总结(临界资源问题等)实现多线程的3种方法线程的优先级yield和sleep临界资源问题多线程下的单例模式(DCL懒汉式)volatile关键字线程池,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

最近在准备面试的时候经常会看到Java多线程相关的问题,在准备的过程中也学习到了很多的知识,于是在这里对Java多线程知识进行一个整理。

实现多线程的3种方法

1. 继承Thread类

class MyThread extends Thread{
@Override
public void run(){
for(int x = 0 ; x < 1000 ; x++) {
System.out.println(x);
}
}
}
MyThread thread = new MyThread();
thread.start();

2. 实现Runnable接口

class MyThread implements Runnable{
@Override
public void run(){
for(int x = 0 ; x < 1000 ; x++) {
System.out.println(x);
}
}
}
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread, "myThread");
thread.start();

3. 使用Callable和Future

//方法3 Callable和Future
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int i = 0;
for(;i < 100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
}
MyThread thread = new MyThread();
FutureTask<Integer> ft = new FutureTask<>(thread);
Thread t1 = new Thread(ft, "myThread");
t1.start();
try{
System.out.println(ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

线程的优先级

优先级是[0, 10]的整数,默认是5。更大的优先级只是有更大的概率抢到CPU时间片,并不是一定会优先执行。

Thread t1 = new Thread(()->{
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}, "Thread1")
Thread t2 = new Thread(()->{
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}, "Thread2")
t1.setPriority(10);
t2.setPriority(1);
t1.start();
t2.start();

yield和sleep

yield使线程回到可执行状态,线程会重新开始抢CPU,所以又可能马上又被执行

sleep和yield的异同:

相同:它们都不会释放锁

不同:sleep可以让低优先级的线程得到机会,yield不可以

4. 线程退出

使用一个变量 volatile boolean exit = false 来控制退出

临界资源问题

synchronized和ReentrantLock的使用

使用锁防止多个线程同时访问临界资源

//同步代码段
int num = 100;
Runnable r = ()->{
while(num > 0){
synchronized(""){
//小括号内的锁必须对所有的线程都是一样的
if(num <= 0) return;
System.out.println("Thread.currentThread().getName() + " " + --num);
}
}
}
Thread t1 = new Thread(r, "thread1");
Thread t2 = new Thread(r, "thread2");
Thread t3 = new Thread(r, "thread3");
//同步方法(this是锁)
private synchronized void decrease(){
if(num <= 0) return;
System.out.println("Thread.currentThread().getName() + " " + --num);
}
//锁对象
ReentrantLock lock = new ReentrantLock();
lock.lock();
if(num <= 0) return;
System.out.println("Thread.currentThread().getName() + " " + --num);
lock.unlock();

死锁

多个线程彼此持有对方所需要的所对象,而不释放自己的锁

顺便提一下死锁发生的四个条件:互斥、占有并等待、非抢占、循环等待

public static void main(String[] args) {
Runnable runnable1 = ()->{
synchronized ("A"){
System.out.println("线程1:A锁");
synchronized ("B"){
System.out.println("线程2:B锁");
}
}
};
Runnable runnable2 = ()->{
synchronized ("B"){
System.out.println("线程2:B锁");
synchronized ("A"){
System.out.println("线程2:A锁");
}
}
};
new Thread(runnable1, "线程1").start();
new Thread(runnable2, "线程2").start();
}

死锁的解决方法:

wait:释放当前的锁,让出CPU,进入等待

notify/notifyAll:才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行

public static void main(String[] args) {
Runnable runnable1 = ()->{
synchronized ("A"){
System.out.println("线程1:A锁");
try {
"A".wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized ("B"){
System.out.println("线程2:B锁");
}
}
};
Runnable runnable2 = ()->{
synchronized ("B"){
System.out.println("线程2:B锁");
synchronized ("A"){
System.out.println("线程2:A锁");
}
"A".notify();
}
};
new Thread(runnable1, "线程1").start();
new Thread(runnable2, "线程2").start();
}

生产者消费者问题

class Product{
private String name;
public Product(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
class ProductPool{
private List<Product> productList;
private int maxSize;
public ProductPool(int maxSize){
this.productList = new LinkedList<>();
this.maxSize = maxSize;
}
public synchronized void push(Product product){
if(this.productList.size() == maxSize){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.productList.add(product);
this.notifyAll();
}
public synchronized Product pop(){
if(this.productList.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Product product = this.productList.remove(0);
this.notifyAll();
return product;
}
}
class Producer extends Thread{
private ProductPool productPool;
public Producer(ProductPool productPool){
this.productPool = productPool;
}
@Override
public void run() {
for(int i = 0; i < 100; i++){
String name = i + "号产品";
Product product = new Product(name);
System.out.println("生产" + name);
this.productPool.push(product);
}
}
}
class Consumer extends Thread{
private ProductPool productPool;
public Consumer(ProductPool productPool){
this.productPool = productPool;
}
@Override
public void run(){
for(int i = 0; i < 100; i++){
Product product = this.productPool.pop();
System.out.println("消费" + product.getName());
}
}
}
public static void main(String[] args) {
ProductPool productPool = new ProductPool(10);
new Producer(productPool).start();
new Consumer(productPool).start();
}

多线程下的单例模式(DCL懒汉式)

class DCLSingleton {
private volatile static DCLSingleton instance = null;
public static DCLSingleton getInstance() {
if(null == instance) {
synchronized (DCLSingleton.class) {
if(null == instance) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}

volatile关键字

一篇很好的博客

作用:如果一个变量被volatile修饰,当一个线程修改了该变量的值,这个变量的新值对其他线程来说是立即可见的。所以它可以用作线程退出的flag。

但使用volatile时需要注意的是:volatile不能保证原子性。如下面这个自增的例子。

public volatile int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) {
final Main test = new Main();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<10;j++)
test.increase();
};
}.start();
}
System.out.println(test.inc); //输出总是小于100
}

上面这个例子的输出总是小于100。这是因为自增操作包括了读、+1、写入内存,所以不具备原子性。

volatile还可以保证有序性。volatile变量前面的操作全部已经执行,结果对后面可见,后面的操作没有进行。在进行指令优化时,volatile变量前面的语句不会放到后面,后面也不会放到前面。

volatile在某些情况下性能要比synchronized好。要使用它就要保证它的写操作不依赖当前值,也不能用它来赋值。

线程池

ExecutorService service = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
service.execute(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("execute");
}
}
});
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
for(int i = 0; i < 100; i++){
System.out.println("submit方式");
}
return 100;
}
});
try {
Integer res = future.get();
} catch (InterruptedException e){
e.printStackTrace();
} catch (ExecutionException e){
e.printStackTrace();
}
service.shutdown();

 

最后

以上就是甜蜜刺猬为你收集整理的Java多线程及相关知识总结(临界资源问题等)实现多线程的3种方法线程的优先级yield和sleep临界资源问题多线程下的单例模式(DCL懒汉式)volatile关键字线程池的全部内容,希望文章能够帮你解决Java多线程及相关知识总结(临界资源问题等)实现多线程的3种方法线程的优先级yield和sleep临界资源问题多线程下的单例模式(DCL懒汉式)volatile关键字线程池所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部