一、多线程线程安全性
1、非线程安全(重要!)
当多个线程对同一个对象中的实例变量进行并发的访问时候,可能会出现脏读(dirty read)的情况,这样就属于非线程安全,此时可以使用Synchronized进行同步处理,只有当一个线程处理完毕业务逻辑之后,其他线程才能进行处理。
注意:非线程安全问题只存在实例变量中,局部变量则不存在线程安全问题;只有对同一个对象中的实例变量进行访问才能出现非线程安全,不同的对象会产生多个同步对象锁,不会出现非线程安全性。
//线程测试的业务类 public class TestSynch { public String name = "wuyanzu"; public void printAndChangeName(){ System.out.println("Thread name:"+ Thread.currentThread().getName()+" userName:"+name); name = "fengdelun"; } public void printName() throws InterruptedException { Thread.currentThread().sleep(1000); System.out.println("Thread name:"+ Thread.currentThread().getName()+" userName:"+name); } }
//A线程类 public class ThreadA implements Runnable { public TestSynch testSynch; //实例变量 public ThreadA(TestSynch testSynch) { this.testSynch = testSynch; } @Override public void run() { testSynch.printAndChangeName(); } }
//B线程类 public class ThreadB implements Runnable { public TestSynch testSynch; public ThreadB(TestSynch testSynch) { this.testSynch = testSynch; } @Override public void run() { try { testSynch.printName(); //调用打印用户名的功能 } catch (InterruptedException e) { e.printStackTrace(); } } }
public class MyTest { public static void main(String[] args) { TestSynch testSynch = new TestSynch(); //线程A和线程B中的实例变量都是同一个对象testSynch ThreadA threadA = new ThreadA(testSynch); ThreadB threadB = new ThreadB(testSynch); Thread A = new Thread(threadA); A.setName("A"); Thread B = new Thread(threadB); B.setName("B"); A.start(); B.start(); } }
打印结果:
Thread name:A userName:wuyanzu
Thread name:B userName:fengdelun
总结:对于上述例子中,虽然线程A和线程B都调用了TestSynch中不同方法,但是都是对同一个对象testSynch,对象中的方法都涉及到了实例变量,所以会出现非线程安全问题(dirty read)。
如果MyTest更改为如下:
public class MyTest { public static void main(String[] args) { TestSynch testSynchA = new TestSynch(); TestSynch testSynchB = new TestSynch(); //线程A和线程B中的实例变量此时是TestSynch的不同对象 ThreadA threadA = new ThreadA(testSynchA); ThreadB threadB = new ThreadB(testSynchB); Thread A = new Thread(threadA); A.setName("A"); Thread B = new Thread(threadB); B.setName("B"); A.start(); B.start(); } }
打印结果:
Thread name:A userName:wuyanzu
Thread name:B userName:wuyanzu
原因:此时A线程和B线程操作的都不是同一个对象中实例变量,所以不会出现线程安全的问题。
2、使用
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
第一种方式编写和启动都简单,但是第二、三种方式更灵活,可以同时继承其他类和实现其他接口