目录
4:线程安全:线程安全问题来源于两个线程同时存取单一对象的数据。
——(1)使用同步方法(同步方法就是在方法前面修饰synchronized关键字的方法) synchronized void f(){}
——(2)使用同步块(在Java中提供同步机制,可以有效地防止,资源冲突。同步机制使用synchronized关键字)
4:jdk在1.5之后为我提供了现成的线程池工具,学习使用他们。
一:线程同步
1:什么是同步:
当两个或多个线程需要访问共享的资源时,他们需要一某种方式确保每次只有一个线程使用资源,
实现这一目的的过程称为同步。
2:java中的多线程同步是什么?
即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
3:“非线程安全”问题存在于“实例变量”中。
方法内部的私有变量,则不存在“非线程安全”问题,也就是“线程安全”。
如果两个线程同时操作对象中的实例变量,则会出现“非线程安全”,解决方法就是在方法前加synchronized关键字即可。
4:线程安全:线程安全问题来源于两个线程同时存取单一对象的数据。
(1)代码: 使用多线程模拟火车站售票系统来演示同步问题。
public class ThreadSafeTest implements Runnable{
int num=10;
public void run(){
while(true){
if(num>0){
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("车票还有"+ num-- +"张");
}
}
}
public static void main(String[] args){
ThreadSafeTest t=new ThreadSafeTest();
Thread ta=new Thread(t);
Thread tb=new Thread(t);
Thread tc=new Thread(t);
Thread td=new Thread(t);
ta.start();
tb.start();
tc.start();
td.start();
}
}
(2)运行结果:
通过运行结果可以看出,在运行时,有的线程已经操作了线程是票数-1,但是仍然会有其他的线程
操作共享资源,是数据出现了错误。
二:线程同步机制(同步有2种实现的方法)
1:使用synchronized关键字
——(1)使用同步方法(同步方法就是在方法前面修饰synchronized关键字的方法) synchronized void f(){}
——同步方法调用时的注意事项:
当某个对象调用同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。
必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。
代码:使用了同步方法
public class ThreadSafeTest implements Runnable{
int num=10;
public synchronized void doit(){
if(num>0){
try{
Thread.sleep(10);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("车票还有"+num--+"张");
}
}
public void run(){
while(true){
doit();
}
}
public static void main(String[] args){
ThreadSafeTest t=new ThreadSafeTest();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
——(2)使用同步块(在Java中提供同步机制,可以有效地防止,资源冲突。同步机制使用synchronized关键字)
————(1)什么情况下使用同步块:
类是有第三方创建的,不能访问类的源代码,因此不能为类中合适方法添加synchronized修饰符。
某个类没有针对多线程访问进行设计,即类没有同步方法,而又希望同步对类的访问。
public class ThreadSafeTest implements Runnable{
int num=10;
public void run(){
while(true){
synchronized (""){
if(num>0){
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("票"+--num);
}
}
}
}
public static void main(String[] args){
ThreadSafeTest t=new ThreadSafeTest();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
2:wait()与notify()方法。
三:线程安全的类
如果有一个类,其方法都是有synchronized修饰的,那么该类就叫线程安全的类。
同一时间,只有一个线程能够进入这种类的一个实例去修改数据,进而保证了这个实例中的数据的
安全,(不会同时被多线程修改而变成脏数据)
四:线程池
1:为什么引入线程池?
多线程的软件设计方法确实可以最大限度的发挥现代多核处理器的计算能力,提高生产系统的吞吐量和性能。
每一个线程的启动和结束都是比较消耗时间和占用资源的。
如果在系统中用到了很多的线程,大量的启动和结束动作会导致系统的性能变卡,响应变慢。
为了解决这个问题,引入线程池这种设计思想。
2:在使用线程池后,创建线程变成从从线程池获得空闲线程,关闭线程变成了向线程池中归还线程,提高了线程的复用。
线程池的模式很像生产者消费者模式,消费的对象是一个一个的能够运行的任务
3:线程池设计思路(避免重复的创建和销毁线程)
线程池的思路和生产者消费者模型是很接近的。
1. 准备一个任务容器
2. 一次性启动10个 消费者线程
3. 刚开始任务容器是空的,所以线程都wait在上面。
4. 直到一个外部线程往这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒notify
5. 这个消费者线程取出“任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来。
6. 如果短时间内,有较多的任务加入,那么就会有多个线程被唤醒,去执行这些任务。
在整个过程中,都不需要创建新的线程,而是循环使用这些已经存在的线程。
4:jdk在1.5之后为我提供了现成的线程池工具,学习使用他们。
(1)Executors线程池工厂能创建哪些线程池。
(2)如何手动创建线程池。
(3)如何扩展线程池
(4)如何优化线程池的异常信息
(5)如何设计线程池中的线程数量