多线程中的安全问题
synchronized
synchronized格式
- 同步代码块的格式:
synchronized (锁对象){...}
- 非静态同步方法的格式:
public synchronized 方法返回值 方法名称 (){....}
- 静态同步方法的格式:
public static synchronized 方法返回值 方法名称(){....}
synchronized的同步代码块
1.使用同步代码块解决多线程安全问题
***使用同步代码块实现多线程卖票***
使用继承Thread实现多窗口卖票
Thread实现类:
//使用继承Thread类实现卖票
public class MyThread extends Thread{
public static int ticket = 20;
public static Object obj = new Object();//设置锁对象
public MyThread(String name) {
super(name);
}
public void run(){
while (true){
synchronized (obj){
// synchronized (MyThread.class){
if (ticket <= 0){
System.out.println("票卖完了");
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"张票");
ticket--;
}
}
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread("李哈哈");
MyThread myThread2 = new MyThread("李嘿嘿");
myThread1.start();
myThread2.start();
}
}
2.使用同步代码块解决多个入口执行共享资源问题
多个入口操作共享资源,实现同步代码块多窗口卖票
Thread实现类:
//实现多个同步代码块进行多入口操作共享资源
public class MyThread extends Thread{
public static int ticket = 60;
public MyThread(String name) {
super(name);
}
public void run(){
while (true){
if (ticket == 0){
break;
}
if (ticket % 2 == 0){
synchronized (this){//必须保证和所有的同步代码块的锁对象相同
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
ticket--;
}
}
}else{
synchronized (this){//必须保证和所有的同步代码块的锁对象相同
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
ticket--;
}
}
}
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread("李哈哈");
MyThread myThread2 = new MyThread("李嘿嘿");
myThread1.start();
myThread2.start();
}
}
synchronized的非静态同步方法
1.使用非静态同步方法解决多线程安全问题
***使用静态同步方法***
使用接口Runnable和非静态同步方法多窗口卖票
Runnable接口的实现类:
public class MyRunnable implements Runnable{
public static int num = 20;
@Override
public void run() {
while (true){
if (num == 0){
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
method();
}
}
}
public synchronized void method(){//锁的对象默认是this
if (num > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
num--;
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "窗口1");
Thread t2 = new Thread(myRunnable, "窗口2");
t1.start();
t2.start();
}
}
2.使用非静态同步方法解决多个入口执行共享资源问题
多个入口操作共享资源,实现非静态同步方法多窗口卖票
//使用非静态实现多入口多窗口卖票
public class MyRunnable implements Runnable{
public static int num = 20;
@Override
public void run() {
while (true){
if (num == 0){
break;
}else if (num % 2 == 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
method1();
}else {
method();
}
}
}
public synchronized void method(){//锁的对象默认是this
if (num > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
num--;
}
}
public synchronized void method1(){//锁的对象默认是this
if (num > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
num--;
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "窗口1");
Thread t2 = new Thread(myRunnable, "窗口2");
t1.start();
t2.start();
}
}
synchronized的静态同步方法
1.使用静态同步方法解决多线程安全问题
使用静态同步方法
使用Runnable接口和静态同步方法实现多窗口卖票
//使用静态同步方法实现多窗口卖票
public class MyRunnable implements Runnable {
public static int ticket = 40;
@Override
public void run() {
while (true){
if (ticket == 0){
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
method();
}
}
}
public static synchronized void method(){
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
ticket--;
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "小姐姐");
Thread t2 = new Thread(myRunnable, "大姐姐");
t1.start();
t2.start();
}
}
2.使用非静态同步方法解决多个入口执行共享资源问题
使用多个静态同步方法实现多卤肉执行共享资源
//使用静态同步方法实现多入口多窗口卖票
public class MyRunnable implements Runnable{
public static int num = 20;
@Override
public void run() {
while (true){
if (num == 0){
break;
}else if (num % 2 == 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
method1();
}else {
method();
}
}
}
public static synchronized void method(){//锁的对象默认是当前类的CLass对象
if (num > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
num--;
}
}
public static synchronized void method1(){//锁的对象默认是当前类的CLass对象
if (num > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
num--;
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "a");
Thread t2 = new Thread(myRunnable, "b");
t1.start();
t2.start();
}
}
多入口和多窗口卖票的不同情况
多入口操作共享资源时,使用同步代码块和静态同步方法,非静态同步方法的搭配使用,实现多窗口卖票
多入口的操作共享资源的多种情况:
- 多个入口都是同步代码块时:需要保持多个同步代码块对的锁对象是同一个。
- 多个入口有同步代码块和非静态同步方法时:需要将同步代码块中的锁对象修改为this,保证同步代码块和非静态同步方法的锁对象一致
- 多个入口有同步代码块和静态同步方法时:需要将同步代码块中的锁对象修改为当前类的Class对象,有两种方式修改同步代码块中的锁改为Class对象(前类名.class或者this.getClass),这样保证了同步代码块和静态同步方法的锁对象一致。
代码实现
1.同步代码块+非静态同步方法+多入口操作共同资源
思路:
需要使得同步代码块中的锁对象和非静态同步方法中的锁对象相同,但是非静态方法中的锁默认为this,当前类,所以只有修改同步代码块中的锁对象也为this,才能保证两者锁对象相同,多个入口的锁应该使用相同的锁,所以使用同一种非静态同步方法即可。
代码块:
同步代码块+非静态同步方法
//使用同步方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和同步方法
public class MyRunnable implements Runnable{
public static int ticket = 40;
@Override
public void run() {
while (true){
if (ticket % 3 == 0){
// 使用同步代码块
synchronized (this){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
ticket--;
}
}
}else{
// 使用同步方法
method();
}
}
}
public synchronized void method(){//同步方法的锁对象默认是this
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
ticket--;
}
}
}
2.同步代码块+静态同步方法+多入口操作共同资源
思路:
需要同步代码块中的锁对象和静态同步方法的锁对象保持一致,静态同步方法中的锁对象默认为当前类的Class对象,所以修改同步代码块中的锁对象使得两者保持一致即可。
代码块:
同步代码块+静态同步方法+多入口操作共享资源
//使用同步静态方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和静态同步方法
public class MyRunnable implements Runnable{
public static int ticket = 40;
@Override
public void run() {
while (true){
if (ticket % 3 == 0){
// 使用同步代码块
// synchronized (MyRunnable.class){
synchronized (this.getClass()){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
ticket--;
}
}
}else{
// 使用静态同步方法
method();
}
}
}
public static synchronized void method(){//静态同步方法的锁对象默认是当前类的class对象
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
ticket--;
}
}
}
Lock锁
使用Lock实现多线程卖票:
Lock解决多线程安全问题
//使用锁实现多线程卖票
public class MyRunnable implements Runnable{
private static int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
if (ticket == 0){
System.out.println("票卖完了");
break;
}else {
lock.lock();
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
ticket--;
}
}
lock.unlock();
}
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable,"窗口1");
Thread t2 = new Thread(myRunnable,"窗口2");
Thread t3 = new Thread(myRunnable,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
本文来自博客园,作者:乖乖在努力,转载请注明原文链接:https://www.cnblogs.com/lihaha520-1314/p/16594561.html