Java多线程复习
线程创建
-
实现runnable类(推荐)
public class TreadLearningIR implements Runnable { private String name; public TreadLearningIR(String name){ this.name=name; } @Override public void run() { for(int i=0;i<5;i++){ System.out.println(name+"运行: "+i); } } public static void main(String[] args) { new Thread(new TreadLearningIR("A")).start(); new Thread(new TreadLearningIR("B")).start(); } }
-
继承Thread类
public class ThreadLearningET extends Thread { private String name; public ThreadLearningET(String name){ this.name=name; } @Override public void run() { for(int i=0;i<5;i++){ System.out.println(name+"运行: "+i); } } public static void main(String[] args) { ThreadLearningET threadLearning1 = new ThreadLearningET("A"); ThreadLearningET threadLearning2 = new ThreadLearningET("B"); threadLearning1.start(); threadLearning2.start(); } }
锁 synchronize
synchronize
synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
线程安全问题
线程安全出现的原因:多个线程抢占统一资源,当一个线程抢到CPU之后还没执行完,CPU又被抢走,造成数据被重复调用。
-
模拟线程出现问题:
public class Demo12 { public static void main(String[] args) { //任务对象 Ticket ticket = new Ticket(); //将任务与线程绑定 Thread t0 = new Thread(ticket); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); //开启线程 t0.start(); t1.start(); t2.start(); t3.start(); } } class Ticket implements Runnable{ int sum = 20; boolean flag = true; public void run() { while (flag) { //让当前的线程睡100毫秒 //作用:让他暂时让出cpu try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (sum > 0) { System.out.println("剩余 票数:"+ --sum); }else { flag = ! flag; } } } } //输出结果 多个值被同时抢到 剩余 票数:19 剩余 票数:17 剩余 票数:18 剩余 票数:19 剩余 票数:16 剩余 票数:15 剩余 票数:14 剩余 票数:13 剩余 票数:12 剩余 票数:11 剩余 票数:10 剩余 票数:10 剩余 票数:8 剩余 票数:9 剩余 票数:7 剩余 票数:7 剩余 票数:6 剩余 票数:6 剩余 票数:5 剩余 票数:5 剩余 票数:4 剩余 票数:4 剩余 票数:3 剩余 票数:3 剩余 票数:1 剩余 票数:2 剩余 票数:0 剩余 票数:0
-
加锁解决线程安全问题
public class Demo13 { public static void main(String[] args) { //任务对象 Ticket1 ticket = new Ticket1(); //将任务与线程绑定 Thread t0 = new Thread(ticket); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); //开启线程 t0.start(); t1.start(); t2.start(); t3.start(); } } //任务类 class Ticket1 implements Runnable{ int sum = 20; boolean flag = true; //让Object类型的对象临时充当锁 Object object = new Object(); public void run() { while (flag) { //让当前的线程睡100毫秒 //作用:让他暂时让出cpu try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /* * 锁的条件: * 1.锁必须是对象 普通的对象/this/字节码文件 * 2.要被所有的线程共享 * 注意:字节码文件的使用范围太大,一般不建议使用. */ synchronized (this) {//线程互斥 if (sum > 0) { System.out.println("剩余 票数:"+ --sum); }else { flag = ! flag; } } } } } //运行结果 剩余 票数:19 剩余 票数:18 剩余 票数:17 剩余 票数:16 剩余 票数:15 剩余 票数:14 剩余 票数:13 剩余 票数:12 剩余 票数:11 剩余 票数:10 剩余 票数:9 剩余 票数:8 剩余 票数:7 剩余 票数:6 剩余 票数:5 剩余 票数:4 剩余 票数:3 剩余 票数:2 剩余 票数:1 剩余 票数:0
创建锁的条件
- 锁必须是对象 普通的对象/this/字节码文件
- 要被所有的线程共享
同步代码块\同步函数
- 同步代码块儿使用更加的灵活,只给需要同步的部分代码同步即可,而同步函数是给这个函数内的所有代码同步.由于处于同步的代码越少越好,所以最好使用同步代码块儿
class Bank {
int money;
//使用同步代码块儿
public void addMoney(int money) {
synchronized (this) {
this.money += money;
System.out.println(this.money);
}
}
}
class Bank {
int money;
//使用同步函数
//非静态的同步函数
//在synchronized后面默认有一个this
public synchronized void addMoney(int money) {
this.money += money;
System.out.println(this.money);
}
//静态的同步函数
//在synchronized后面默认有一个当前类的字节码文件-----Bank.class
public synchronized static void addMoney(int money) {
this.money += money;
System.out.println(this.money);
}
}
线程之间通信
-
普通线程通信控制(对共享数据加锁)
//数据类 class Des{ String name; String sex; } //输入任务 class Input implements Runnable{ Des des; public Input(Des des) { this.des = des; } public void run() { int i=0; while (true) { /* * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的 * 给两个任务加一把锁:可以是des或者Object.class * 分析: * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误. * 使用des最合适,因为他只被当前的两个任务共享. * *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步. */ synchronized (des) { if (i==0) { des.name = "万表哥"; des.sex = "男"; }else { des.name = "蔡表妹"; des.sex = "女"; } i=(i+1)%2; } } } } //输出任务 class Output implements Runnable{ Des des; public Output(Des des) { this.des = des; } public void run() { while (true) { synchronized (des) { System.out.println("姓名:"+des.name+" 性别:"+des.sex); } } } }
-
进程间三态控制
-
wait():让当前的线程变成等待的状态,失去抢占CPU的资格,等待被唤醒
-
notify():让当前的线程从等待状态唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程 )
-
notifyAll():唤醒的是同一把锁下的所有线程
//数据类 class Des1{ String name; String sex; boolean flag = false;//用于唤醒和等待之间的切换 } //输入任务 class Input1 implements Runnable{ Des1 des; public Input1(Des1 des) { this.des = des; } public void run() { int i=0; while (true) { /* * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的 * 给两个任务加一把锁:可以是dies或者Object.class * 分析: * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误. * 使用des最合适,因为他只被当前的两个任务共享. * *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步. */ synchronized (des) { if (des.flag == true) {//如果是true,让当前的线程处于等待状态 try { des.wait();//当执行这行代码的时候,这里对应的是哪个线程,就操作的是哪个线程 } catch (InterruptedException e) { e.printStackTrace(); } } if (i==0) { des.name = "万表哥"; des.sex = "男"; }else { des.name = "蔡表妹"; des.sex = "女"; } i=(i+1)%2; des.flag = ! des.flag; des.notify();//唤醒的是同一把锁下的线程,因为现在只有一个输入线程,一个输出线程.所以这里唤醒的是输出线程 //当线程池中没有被当前的锁标记的线程可唤醒时,我们成为空唤醒,空唤醒不影响程序的执行. } } } } //输出任务 class Output1 implements Runnable{ Des1 des; public Output1(Des1 des) { this.des = des; } public void run() { while (true) { synchronized (des) { if (des.flag == false) {//让输出线程等待 try { des.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("姓名:"+des.name+" 性别:"+des.sex); des.flag = ! des.flag; des.notify();//唤醒输入线程 } } } }
-
生产者\消费者设计模式
单生产者、单消费者
public class Demo9 {
public static void main(String[] args) {
//准备数据
Product product = new Product();
//准备任务
Producer producer = new Producer(product);
Consumer consumer = new Consumer(product);
//准备线程
Thread proThread = new Thread(producer);
Thread conThread = new Thread(consumer);
//开启线程
proThread.start();
conThread.start();
}
}
//创建产品
class Product{
String name;//产品的名字
double price;//产品的价格
int count;//生产的产品数量
//标识
boolean flag = false;
//准备生产
public synchronized void setProduce(String name,double price){
if (flag == true) {
try {
wait();//让生产线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
count++;
flag = ! flag;
notify();//唤醒消费线程
}
//准备消费
public synchronized void getConsume() {
if (flag == false) {
try {
wait();//让消费线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
//唤醒生产线程
flag = ! flag;
notify();
}
}
//创建生产任务
class Producer implements Runnable{
Product product;
public Producer(Product product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer implements Runnable{
Product product;
public Consumer(Product product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.getConsume();
}
}
}
多生产者、多消费者
public class Demo10 {
public static void main(String[] args) {
//准备数据
Product1 product = new Product1();
//准备任务
Producer1 producer = new Producer1(product);
Consumer1 consumer = new Consumer1(product);
//准备线程
Thread proThread1 = new Thread(producer);
Thread proThread2 = new Thread(producer);
Thread conThread1 = new Thread(consumer);
Thread conThread2 = new Thread(consumer);
//开启线程
proThread1.start();
conThread1.start();
proThread2.start();
conThread2.start();
}
}
//创建产品
class Product1{
String name;//产品的名字
double price;//产品的价格
int count;//生产的产品数量
//标识
boolean flag = false;
//准备生产
public synchronized void setProduce(String name,double price){
while (flag == true) {
try {
wait();//让生产线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
count++;
flag = ! flag;
//notify();//唤醒消费线程
notifyAll();
}
//准备消费
public synchronized void getConsume() {
while (flag == false) {
try {
wait();//让消费线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
//唤醒生产线程
flag = ! flag;
//notify();
notifyAll();
}
}
//创建生产任务
class Producer1 implements Runnable{
Product1 product;
public Producer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer1 implements Runnable{
Product1 product;
public Consumer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.getConsume();
}
}
}
Lock
比synchronize效率更高的锁机制
-
使用场景
当进行多生产者多消费者的功能时,使用Lock,其他的都使用synchronized
public class Demo11 {
public static void main(String[] args) {
//准备数据
Product2 product = new Product2();
//准备任务
Producer2 producer = new Producer2(product);
Consumer2 consumer = new Consumer2(product);
//准备线程
Thread proThread1 = new Thread(producer);
Thread proThread2 = new Thread(producer);
Thread conThread1 = new Thread(consumer);
Thread conThread2 = new Thread(consumer);
//开启线程
proThread1.start();
conThread1.start();
proThread2.start();
conThread2.start();
}
}
//创建产品
class Product2{
String name;//产品的名字
double price;//产品的价格
int count;//生产的产品数量
//标识
boolean flag = false;
//创建锁对象
Lock lock = new ReentrantLock();
//用于生产任务的Condition
Condition proCon = lock.newCondition();
//用于消费任务的Condition
Condition conCon = lock.newCondition();
//准备生产
public void setProduce(String name,double price){
try {
lock.lock();//获取锁
while (flag == true) {
try {
proCon.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
count++;
flag = ! flag;
conCon.signal();
}finally {
lock.unlock();//释放锁
}
}
//准备消费
public void getConsume() {
try {
lock.lock();
while (flag == false) {
try {
conCon.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+this.name+" 产品的数量:"+this.count+" 价格:"+this.price);
//唤醒生产线程
flag = ! flag;
proCon.signal();
}finally {
lock.unlock();
}
}
}
//创建生产任务
class Producer2 implements Runnable{
Product2 product;
public Producer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer2 implements Runnable{
Product2 product;
public Consumer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.getConsume();
}
}
}
守护线程
相当于后台线程.依赖于前台线程.正常情况下,当前台线程结束的时候,不管守护线程有没有结束,都会立刻结束.典型的守护线程:垃圾回收线程
public class Demo15 {
public static void main(String[] args) {
MyTest1 myTest1 = new MyTest1();
Thread t0 = new Thread(myTest1);
/*
* 当程序调用setDaemon方法时,并且将参数设置成true.当前线程就变成了守护线层.
* 注意:这个方法一定要在start方法之前调用
*/
t0.setDaemon(true);
t0.start();
int i=0;
while (true) {
if (++i == 10) {
System.out.println(Thread.currentThread().getName()+" 主线程");
break;
}
}
}
}
class MyTest1 implements Runnable{
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+" 子线程");
}
}
}
join
/*
* join()方法:
* 原理:线程一旦调用了join方法,他的优先级会高于主线程.主线程会等当前的线程执行完后再去执行.
* 注意点:优先级只比main线程的高.对其他的线程没有影响.
*
*/
public class Demo16 {
public static void main(String[] args) {
MyTest2 myTest1 = new MyTest2();
Thread t0 = new Thread(myTest1);
Thread t1 = new Thread(myTest1);
t0.start();
t1.start();
/*
* 当线程开始工作后,让t0调用join方法,让他的优先级高于main线程
* 注意:join方法必须在线程开始工作后,执行.
*/
try {
t0.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" 主线程"+i);
}
}
}
class MyTest2 implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" 子线程"+i);
}
}
}