Java复习笔记-多线程

1 线程

线程是一个进程里不同的执行路径。

进程是一个静态的概念,进程开始执行其实是他的主线开始执行了(主线程:main方法)

在同一个时间点上,CPU只有一个线程在执行,真正的多线程:多核

Java的线程是通过Java.lang.Thread类类实现的,可以通过创建Thread的实例来创建新的线程,Thread类里的run方法:你在run里写哪些东西,这个线程就执行哪些东西。start()方法启动一个新的线程。

创建线程的方法:

(1)实现Runnable接口,编译器就知道这是一个线程类。(Thread类本身就实现了Runnable接口)

Thread(Runnable target)

有多态存在,Runnable target是父类的引用,当我们传的时候传的是子类对象,即父类引用指向子类对象。这又是重写,调的是自己的run方法。

(2)继承Thread类

能使用接口就使用接口,因为继承不能多继承。

线程状态转换:

创建-start()(start只是就绪状态,并不是就开始运行了)

 

2 线程同步

Java引入了对象互斥锁,保证共享数据操作的完整性,每个对象都对应于一个可称为“互斥锁”的标记,标记保证在任一时刻,只能由一个线程访问该对象。

//线程同步
public class TestThread4  implements Runnable {
	Timer timer=new Timer();
	public static void main(String[] args) {
		TestThread4  test=new  TestThread4();
		Thread t1=new Thread(test); 
		Thread t2=new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
	
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
	
}
 
class Timer {
	private static int num=0;
//	
	public void add(String name) {
	synchronized(this) {//锁定某一个东西 锁定synchronized大括号里的东西  ----互斥锁
			num++;
			try {Thread.sleep(1);}
			catch(InterruptedException e) {}
			System.out.println(name+",你是第"+num+"个使用timer的线程");
		}
	}
}

 
 //另一种写法
/*class Timer {
	private static int num=0;
	
	public synchronized void add(String name) {//在执行这个方法时候,锁定当前对象(即锁定this)
		
			num++;
			try {Thread.sleep(1);}
			catch(InterruptedException e) {}
			System.out.println(name+",你是第"+num+"个使用timer的线程");
		
	}
}*/

分析:t1和t2都是传了test这个对象产生的,它们执行的是同一个test对象的run方法,而且run里面是同一个timer对象,也就是说他们要访问同一个timer对象,执行同一个timer的add方法,如果不加锁,输出结果会是:

t1,你是第2个使用timer的线程

t2,你是第2个使用timer的线程

因为在t1的执行过程种,在调用add方法,在num++变为1后,t1休眠了,这时候t2也调用了add方法,num++,因为他们俩是同一个test,同一个timer,同一个num,所以当t1醒来过后,num已经是2了。

这里为了看的更清楚,写了个sleep()方法,实际如果不写,实际情况中也有很多这种访问同一个资源导致不一致的情况。

加了锁过后,输出正确:

t1,你是第1个使用timer的线程

t2,你是第2个使用timer的线程

synchronized是关键字,来与对象的互斥锁联系,当某个对象synchronized修饰时,表面该对象在任一时刻只能由一个线程访问。使用方法:

(1) synchronized 方法

      通过在方法声明中加入 synchronized关键字来声明,语法如下:

public  synchronized  void accessVal(int newVal);

      synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。(即锁定了调用这个方法的当前对象【this】)

(2)▪ synchronized块

      synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

      Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。

      synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

synchronized(syncObject)

   { 

   //允许访问控制的代码 

   }

3 死锁

解决:把锁的粒度加粗一点,

4 一道面试题

public class TestThread5 implements Runnable {
	int b=100;
	public synchronized void m1() throws InterruptedException {
		b=1000;
		Thread.sleep(5000);
		System.out.println("b="+b);
	}
    
	public void m2()
	{
                b=2000;
		System.out.println(b);
	}
	
	public void run()
	{
		try {
			m1();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		TestThread5 tt=new TestThread5();
		Thread t=new Thread(tt);
		t.start();
		
		Thread.sleep(1000);//main睡1s,保证t已经执行到m1里了
		tt.m2();
	}
}

      synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,

m1()方法加了锁,所以他要执行的话,就要得到调用该方法对象的锁,m2没有加锁,它可以随便执行,所以上面这个例子,main开始执行,执行到t.start();,这时线程t开始执行,即调用run方法,即调用m1方法,修改b=1000,t开始sleep,这时main方法执行到tt.m2(),调用m2,修改b=2000。

所以这里对于都要修改的b,并没有任何保护措施,另一个线程也可以访问修改,不是光m1加了个锁就行的。

如果两个方法都修改了同样的值,为了保护共享数据,必须都应该加同步。

给m2()加了synchronized后,tt.m2()如果向调用,必须得获得调用该方法的对象的锁,即tt的锁,但是这时候m1()获得了锁,虽然他在休眠,但是m1()没有返回,这个锁是不放开的,所以m2()b必须得等到t线程的m1()执行完了,获得锁后才能执行。

没加锁的方法m2()别的线程(这里的main)还是可以自由访问,只是不能访问m1()里的这些句话了,保证只有一个线程进入m1的方法体里面,如果给m2也加了synchronized,则m2也不能进入了。

5 生产者-消费者问题

6 wait和sleep区别

wait时别的线程可以访问锁定对象(wait时候锁放开了)

---但是调用wait方法时必须锁定该对象

sleep时别的线程也不可以访问锁定多谢(sleep时候也抱着这把锁)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值