Java多线程 - 第二篇

在编写一个类用到线程的时候,就应该想到同步的问题。Java自带了一个synchronized 同步关键字,既可以修饰方法,也可以用作synchronized(this)同步代码块。

我们首先写一个模拟车站买票的程序,来说明问题

class Ticket extends Thread{
	public Ticket(String name){
		super(name);
	}
	int count = 50;
	@Override
	public void run() {
		while(true){
			if(count >0){
				System.out.println(Thread.currentThread().getName()+
						"售出第:"+ count+" 票");
				count--;
			}else{
				System.out.println(Thread.currentThread().getName()+"卖完了...");
				break;
			}
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		Ticket t1 = new Ticket("车站1");
		Ticket t2 = new Ticket("车站2");
		
		t1.start();
		t2.start();
	}
}
运行结果:
/*
车站1售出第:50 票
车站2售出第:50 票
车站1售出第:49 票
车站2售出第:49 票
车站1售出第:48 票
车站2售出第:48 票
车站1售出第:47 票
车站2售出第:47 票
车站1售出第:46 票
车站2售出第:46 票
车站1售出第:45 票
车站2售出第:45 票
车站1售出第:44 票
车站2售出第:44 票
车站2售出第:43 票
车站1售出第:43 票
*/
可以看到, 第50张票卖了 2次,哦?这是为啥呢?

原因就在于,总票数count 非静态 所以,他们卖的是各自对象里面的count ,那么是不是用static修饰一下count就可以了呢

class Ticket extends Thread{
	public Ticket(String name){
		super(name);
	}
	static int count = 50; //添加static 后
	@Override
	public void run() {
		while(true){
			if(count >0){
				System.out.println(Thread.currentThread().getName()+
						"售出第:"+ count+" 票");
				count--;
			}else{
				System.out.println(Thread.currentThread().getName()+"卖完了...");
				break;
			}
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		Ticket t1 = new Ticket("车站1");
		Ticket t2 = new Ticket("车站2");
		
		t1.start();
		t2.start();
	}
}

运行结果:

车站1售出第:50 票
车站1售出第:49 票
车站1售出第:48 票
车站1售出第:47 票
车站1售出第:46 票
车站1售出第:45 票
车站1售出第:44 票
车站1售出第:43 票
车站1售出第:42 票
车站1售出第:41 票
车站1售出第:40 票
车站1售出第:39 票
车站1售出第:38 票
车站1售出第:37 票
车站1售出第:36 票
车站1售出第:35 票
车站1售出第:34 票
车站1售出第:33 票
车站1售出第:32 票
车站1售出第:31 票
车站1售出第:30 票
车站1售出第:29 票
车站1售出第:28 票
车站1售出第:27 票
车站2售出第:50 票
车站2售出第:25 票
车站2售出第:24 票
车站2售出第:23 票
车站2售出第:22 票
车站2售出第:21 票
车站2售出第:20 票
车站2售出第:19 票
车站2售出第:18 票
车站2售出第:17 票
车站2售出第:16 票
车站2售出第:15 票
车站2售出第:14 票
车站2售出第:13 票
车站2售出第:12 票
车站2售出第:11 票
车站2售出第:10 票
车站2售出第:9 票
车站2售出第:8 票
车站2售出第:7 票
车站2售出第:6 票
车站2售出第:5 票
车站2售出第:4 票
车站2售出第:3 票
车站2售出第:2 票
车站2售出第:1 票
车站2卖完了...
车站1售出第:26 票
车站1卖完了...

哦。貌似是解决了票数重复的问题,可是怎么 车站2 卖完了之后,车站1 又售出了26票呢。

仔细分析后发现,原来,当车站1,在获取cpu的执行权后,刚刚 执行了第一句 System.out.println(Thread.currentThread().getName()+
"售出第:"+ count+" 票");
后,还没来得及输出,就被车站2 抢夺了CPU的执行权,当车站2 卖完了之后,CPU的执行权又回到了车站1,然后输出了 车站1售出第:26 票

所以这时候,就用到了synchronized 关键字 

class Ticket extends Thread{
	public Ticket(String name){
		super(name);
	}
	static int count = 50; //添加static 后
	@Override
	public void run() {
		while(true){
			synchronized (this) {
				if(count >0){
					System.out.println(Thread.currentThread().getName()+
							"售出第:"+ count+" 票");
					count--;
				}else{
					System.out.println(Thread.currentThread().getName()+"卖完了...");
					break;
				}
			}
			
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		Ticket t1 = new Ticket("车站1");
		Ticket t2 = new Ticket("车站2");
		
		t1.start();
		t2.start();
	}
}

输出结果:

车站1售出第:50 票
车站1售出第:49 票
车站1售出第:48 票
车站1售出第:47 票
车站1售出第:46 票
车站1售出第:45 票
车站1售出第:44 票
车站1售出第:43 票
车站1售出第:42 票
车站1售出第:41 票
车站1售出第:40 票
车站2售出第:50 票
车站2售出第:39 票
车站2售出第:38 票
车站2售出第:37 票
车站2售出第:36 票
车站2售出第:35 票
车站2售出第:34 票
车站2售出第:33 票
车站2售出第:32 票
车站2售出第:31 票
车站2售出第:30 票
车站2售出第:29 票
车站2售出第:28 票
车站2售出第:27 票
车站2售出第:26 票
车站2售出第:25 票
车站2售出第:24 票
车站2售出第:23 票
车站2售出第:22 票
车站2售出第:21 票
车站2售出第:20 票
车站2售出第:19 票
车站2售出第:18 票
车站2售出第:17 票
车站2售出第:16 票
车站2售出第:15 票
车站2售出第:14 票
车站2售出第:13 票
车站2售出第:12 票
车站2售出第:11 票
车站2售出第:10 票
车站2售出第:9 票
车站2售出第:8 票
车站2售出第:7 票
车站2售出第:6 票
车站2售出第:5 票
车站2售出第:4 票
车站2售出第:3 票
车站2售出第:2 票
车站2售出第:1 票
车站2卖完了...
车站1卖完了...



 看样子是没有重复的票数。票数也没有多卖,这里我用的是 synchronized (this)代码块,其中this 这里必须是对象,在这里this代表的是调用当前对象,synchronized 关键字的意思是,锁住代码块的代码,当多个线程同时进来是,一次只能通过一个线程,只有当前线程运完之后 另一个线程才能进到代码块里运行。

注意的是 synchronized () 括号里面是必须是对象,也可以为字符串"锁"  ,在上面的场景中,只用new String("锁") 则锁不住,因为字符串"锁"是常量,在常量池是由一份是唯一的。而new String("锁") 则是一个对象,并非唯一的,同样说明了一个问题,此时 synchronized ()  括号里面的对象必须是唯一的;


同样 可以通过synchronized  来修饰run方法也可以解决

class Ticket extends Thread{
	public Ticket(String name){
		super(name);
	}
	static int count = 50; //添加static 后
	@Override
	public  synchronized void run() {//<span style="font-family: Arial, Helvetica, sans-serif;">synchronized 同样可以修饰方法</span>
		while(true){	
			if(count >0){
				System.out.println(Thread.currentThread().getName()+
						"售出第:"+ count+" 票");
				count--;
			}else{
				System.out.println(Thread.currentThread().getName()+"卖完了...");
				break;
			}
		
		}
	}
}

运行的结果:

车站1售出第:50 票
车站1售出第:49 票
车站1售出第:48 票
车站1售出第:47 票
车站1售出第:46 票
车站1售出第:45 票
车站1售出第:44 票
车站1售出第:43 票
车站1售出第:42 票
车站1售出第:41 票
车站1售出第:40 票
车站1售出第:39 票
车站1售出第:38 票
车站1售出第:37 票
车站1售出第:36 票
车站1售出第:35 票
车站1售出第:34 票
车站1售出第:33 票
车站1售出第:32 票
车站1售出第:31 票
车站1售出第:30 票
车站1售出第:29 票
车站1售出第:28 票
车站1售出第:27 票
车站1售出第:26 票
车站1售出第:25 票
车站1售出第:24 票
车站1售出第:23 票
车站1售出第:22 票
车站1售出第:21 票
车站1售出第:20 票
车站1售出第:19 票
车站1售出第:18 票
车站1售出第:17 票
车站1售出第:16 票
车站1售出第:15 票
车站1售出第:14 票
车站1售出第:13 票
车站1售出第:12 票
车站1售出第:11 票
车站1售出第:10 票
车站1售出第:9 票
车站1售出第:8 票
车站1售出第:7 票
车站1售出第:6 票
车站1售出第:5 票
车站1售出第:4 票
车站1售出第:3 票
车站1售出第:2 票
车站1售出第:1 票
车站1卖完了...
车站2卖完了...


同样可以解决问题。当有两个及以上共享的变量,两个及以上的synchronized,一个synchronized 锁住了一个变量,而另一个synchronized锁住了另一个变量。两者都不释放变量则会出现死锁现象 。

T1锁住了L1,T2锁住L2。 T1 申请锁L2,而不释放L1。而T2 申请锁L1,而不释放L2。出现死锁

死锁现象并不是一定会发生,只是有一定的几率,所以开发中要避免死锁。


同步代码块要注意的事项:
1. 锁对象可以是任意的一个对象。
2. 一个线程在同步代码块中sleep了,并不会释放锁对象。
3. 如果不存在着线程安全问题,千万不要使用同步代码块,因为会降低效率。
4. 锁对象必须是多线程共享的一个资源,否则锁不住。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值