线程的同步
在Java中,通过同步机制,来解决线程的安全问题
同步代码块
synchronized(同步监视器){//需要被同步的代码}说明:操作贡献数据的代码,就是需要被同步的代码同步监视器:锁,一般来说任何类的对象都可以作为锁要求:多线程必须使用同一把锁在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器
同步代码块解决实现接口的线程安全问题
/*** @author hsy* @date 2022/12/19*/class Windows implements Runnable{private int ticket = 100;Object bj = new Object();@Overridepublic void run() {while (true){synchronized (bj) { //也可以用synchronized(this)if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+":买票,票号为:" + ticket);ticket--;} else {break;}}}}}public class WindowTest {public static void main(String[] args) {Windows w = new Windows();Thread w2 = new Thread(w);Thread w3 = new Thread(w);Thread w1 = new Thread(w);w1.setName("窗口1");w2.setName("窗口2");w3.setName("窗口3");w1.start();w2.start();w3.start();}}
同步代码块解决继承的线程安全问题
/*** 创建多线程:方式二:实现Runnable接口* @author hsy* @date 2022/12/19*/class Window2 extends Thread{private static int ticket = 100;private static Object obj = new Object();@Overridepublic void run() {while (true){synchronized (obj){if(ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(getName()+":买票,票号:"+ticket);ticket--;}else {break;}}}}}public class WindowTest2 {public static void main(String[] args) {Window2 t1 = new Window2();Window2 t2 = new Window2();Window2 t3 = new Window2();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
同步方法
//在方法声明中加上synchronizedprivate synchronized void show(){//方法体}
同步方法解决实现接口的线程安全问题
/*** @author hsy* @date 2022/12/19*/class Windows3 implements Runnable{private int ticket = 100;@Overridepublic void run() {while (true){show();}}private synchronized void show(){if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+":买票,票号为:" + ticket);ticket--;}}}public class WindowTest3 {public static void main(String[] args) {Windows3 w = new Windows3();Thread w2 = new Thread(w);Thread w3 = new Thread(w);Thread w1 = new Thread(w);w1.setName("窗口1");w2.setName("窗口2");w3.setName("窗口3");w1.start();w2.start();w3.start();}}
同步方法解决继承的线程安全问题
/*** @author hsy* @date 2022/12/19*/class Window4 extends Thread{private static int ticket = 100;@Overridepublic void run() {while (true){show();}}private static synchronized void show(){if(ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+":买票,票号:"+ticket);ticket--;}}}public class WindowTest4 {public static void main(String[] args) {Window4 t1 = new Window4();Window4 t2 = new Window4();Window4 t3 = new Window4();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
关于同步方法:
- 同步方法仍然涉及到同步监视器本身,只是不需要我们显示的声明
- 非静态的同步方法,同步监视器是this
静态的同步方法,同步监视器是:当前类本身
同步的方式解决了线程的安全问题,操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低。
死锁问题
死锁:
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就会形成线程的死锁
- 出现死锁之后不会出现异常,不会出现提示,只是所有的线程都处在阻塞状态,无法继续
解决方法:
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
实例:
/*** 演示死锁问题* @author hsy* @date 2022/12/20*/public class ThreadTest {public static void main(String[] args) {StringBuffer s1 = new StringBuffer();StringBuffer s2 = new StringBuffer();new Thread(){@Overridepublic void run() {synchronized (s1){s1.append("a");s2.append("1");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (s2){s1.append("b");s2.append("2");System.out.println(s1);System.out.println(s2);}}}}.start();new Thread(new Runnable() {@Overridepublic void run() {synchronized (s2){s1.append("c");s2.append("3");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (s1){s1.append("d");s2.append("4");System.out.println(s1);System.out.println(s2);}}}}).start();}}
Lock(锁)
- 从JDK5.0开始,Java提供了更强大的线程同步机制———-通过显示定义同步锁对象来实现对象,同步锁使用Lock对象充当
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象ReentrantLock实现了Lock,它拥有与synchronized相同的并发性和内存语句,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁
synchronized与Lock的一同
相同:二者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码之后,自动释放代码监视器
Lock需要手动启动同步和关闭同步

