1.通过继承Thread类不能实现资源共享
package com.click369.thread1; /** * 继承Thread类创建多线程 * 总结:继承Thread类无法实现资源共享 */ public class MyThread extends Thread { //共享资源 private int num = 5; @Override public void run() { while (num > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩" + (--num) + "张"); } } } package com.click369.thread1; public class TestMain { public static void main(String[] args) { //创建三条线程 MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); MyThread thread3 = new MyThread(); //为三天线程分别命名 thread1.setName("一号窗"); thread2.setName("二号窗"); thread3.setName("三号窗"); //分别启动三条线程 thread1.start(); thread2.start(); thread3.start(); } }2.实现Runnable接口可以实现资源共享
package com.click369.runnable1; /** * 实现Runnable接口创建多线程 * 总结:实现Runnable接口可以实现资源共享, * 但是当多条线程同时访问同一资源时,会产生数据不一致的错误情况。 */ public class MyThread implements Runnable { //定义共享资源 private int num = 5; @Override public void run() { while (num > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出1张票,还剩" + (--num) + "张"); } } } package com.click369.runnable1; public class TeatMain { public static void main(String[] args) { //创建目标对象 MyThread mt = new MyThread(); //创建三个线程对象 Thread thread1 = new Thread(mt); Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); //分别为三个线程命名 thread1.setName("一号窗"); thread2.setName("二号窗"); thread3.setName("三号窗"); //分别启动三个线程 thread1.start(); thread2.start(); thread3.start(); } }上述虽然实现Runnable接口可以实现资源共享,但是会出现数据错误情况。
总结:当多条线程同时访问统一资源时,会产生数据不一致的错误情况。为了解决这种问题才学到线程安全。
什么是线程同步/线程安全? 线程同步:当多条线程同时访问统一资源时,每次只能由多线程中的其中一条线程进行访问公共资源,当这一条线程访问公共资源时,其他线程都处于等待状态,不能对公共资源进行操作。当访问公共资源的线程完成对公共资源的操作时,其他线程中某一条线程才可以访问资源,具体那条线程有JVM虚拟机分配。其他线程继续等待。这个过程就是线程同步也叫线程安全。
实现线程安全的三种方法:同步代码块、同步方法、使用Lock接口。
1.同步代码块 格式: synchronized(同步对象){
}
package safety.code1; /** * 同步代码块实现线程安全 * 总结:使用同步代码块可以实现线程安全,用时需要设置同步对象 * 如果不清楚具体同步对象,容易写错造成死锁。 */ public class MyThread1 implements Runnable { //定义共享资源 private int num = 8; @Override public void run() { //使用同步代码块 synchronized synchronized (this) { while (num > 0) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出一张,还剩" + (--num) + "张"); } } } } package safety.code1; public class TeatMain1 { public static void main(String[] args) { //创建目标对象 MyThread1 mt = new MyThread1(); //创建三条线程 Thread thread1 = new Thread(mt); Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); //为每条线程命名 thread1.setName("线程一号"); thread2.setName("线程二号"); thread3.setName("线程三号"); //分别启动每条线程 thread1.start(); thread2.start(); thread3.start(); } }2.同步方法 格式: 访问限制修饰符 synchronized 方法返回值类型 方法名称(){
}
package safety.method1; /** * 同步方法实现线程安全 */ public class MyThread1 implements Runnable{ //定义共享资源 private int num=8; private boolean flag=true; @Override public void run() { while(flag){ sellPiao(); } } //使用同步方法实现线程安全 public synchronized void sellPiao(){ if(num>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +"卖出一张票,还剩"+(--num)); }else { flag=false; } } } package safety.method1; public class TestMain1 { public static void main(String[] args) { //定义目标对象 MyThread1 mt = new MyThread1(); //创建三条线程 Thread thread1 = new Thread(mt); Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); //分别为三条线程命名 thread1.setName("一号线程"); thread2.setName("二号线程"); thread3.setName("三号线程"); //分别启动三条线程 thread1.start(); thread2.start(); thread3.start(); } }3.使用Lock接口 lock比synchronized方法和语法获得更广泛的锁定操作。
lock方法:void lock()----获得锁 void unlock()----释放锁 由于Lock是个接口。需要使用子类ReentrantLock() 创建一个ReentrantLock的实例。
package safety.lock1; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 通过lock锁实现线程安全 */ public class MyThread1 implements Runnable { //定义共享资源 private int num = 6; //创建Lock对象 private Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); while (num > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出一张,还剩" + (--num) + "张票"); } lock.unlock(); } } package safety.lock1; public class TestMain1 { public static void main(String[] args) { //创建目标对象 MyThread1 mt = new MyThread1(); //创建多线程 Thread thread1 = new Thread(mt); Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); //为各条线程命名 thread1.setName("一号窗口"); thread2.setName("二号窗口"); thread3.setName("三号窗口"); //分别启动各条线程 thread1.start(); thread2.start(); thread3.start(); } }总结:
| synchronized | lock | | 关键字 | 接口 | | 自动锁定资源 | 手动锁定资源 | | 不灵活 | 灵活 | | 异常会自动释放锁 | 不会自动释放锁,需要在finally中实现释放锁 | | 不能中断锁,必须等待线程执行完成释放锁 | 可以中断锁 |