概述
最近在准备面试的时候经常会看到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关键字线程池所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复