线程的同步
在Java中,通过同步机制,来解决线程的安全问题
同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:操作贡献数据的代码,就是需要被同步的代码
同步监视器:锁,一般来说任何类的对象都可以作为锁
要求:多线程必须使用同一把锁
在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器
同步代码块解决实现接口的线程安全问题
/**
* @author hsy
* @date 2022/12/19
*/
class Windows implements Runnable{
private int ticket = 100;
Object bj = new Object();
@Override
public 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();
@Override
public 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();
}
}
同步方法
//在方法声明中加上synchronized
private synchronized void show(){
//方法体
}
同步方法解决实现接口的线程安全问题
/**
* @author hsy
* @date 2022/12/19
*/
class Windows3 implements Runnable{
private int ticket = 100;
@Override
public 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;
@Override
public 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(){
@Override
public 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() {
@Override
public 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需要手动启动同步和关闭同步