多线程
1. 获取线程相关信息的方法
//获取当前正在执行的线程 Thread main = Thread.currentThread(); //获取线程的名字 String name = main.getName(); //获取唯一标识 long id = main.getId(); //获取线程优先级 int priority = main.getPriority(); //显示是否处于活动状态 boolean isAlive = main.isAlive(); //是否为守护线程 boolean isDaemon = main.isDaemon(); //是否被中断了 boolean isInterrupted = main.isInterrupted();
2. 常用方法
void join() 等待该线程终止。调用该方法的线程会进入阻塞状态,直到该线程结束才会解除阻塞。
void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。
/* * JDK1.8之前有一个要求: * 当一个方法的局部内部类中想引用这个方法的 * 其它局部变量时,该变量要求必须是final的. * 这是JVM内存分配导致的一个问题,在JDK1.8 * 之后不再有. * 例如: * main方法中的局部内部类show当中想引用 * main方法的一个局部变量download,那么 * 该变量必须是final的. * */ final Thread download = new Thread(){ public void run(){ System.out.println("down:开始下载图片..."); for(int i=1;i<=100;i++){ System.out.println("down:"+i+"%"); try { Thread.sleep(50); } catch (InterruptedException e) { } } System.out.println("down:下载完毕!"); isFinish = true; } }; Thread show = new Thread(){ public void run(){ System.out.println("show:开始显示图片..."); //在这里应当先等待下载线程将图片下载完毕 try { download.join(); } catch (InterruptedException e) { e.printStackTrace(); } if(!isFinish){ throw new RuntimeException("图片没有下载完毕!"); } System.out.println("show:图片显示完毕!"); } }; download.start(); show.start();
static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
3. 线程优先级
线程优先级一共有10个等级,分别用数字1-10表示 其中1为最低优先级,10为最高优先级,5为默认值.
理论上线程优先级越高的线程获取CPU时间片的次数越多.实际开发中,将某些执行很重要任务的线程
往往设置的优先级会比较高.
thread1.setPriority(Thread.MAX_PRIORITY);//设置最高优先级 thread2.setPriority(Thread.MIN_PRIORITY);//设置最低优先级
4. 守护线程
多线程并发安全
当多个线程并发操作同一数据时,由于线程切换时机不可控,会导致线程执行逻辑顺序出现混乱,
导致对数据操作未按照程序设计顺序执行而出现逻辑错误,严重时可能导致系统瘫痪.
class MySingleton { //单例模式(在任何情况下,单例类永远只有一个实例存在) private static MySingleton instance = null; private MySingleton(){} public static MySingleton getInstance() { if(instance == null){//懒汉式 instance = new MySingleton(); } return instance; } } public class MyThread extends Thread{ public void run() { System.out.println(MySingleton.getInstance().hashCode()); } public static void main(String[] args) { MyThread[] mts = new MyThread[10]; for(int i = 0 ; i < mts.length ; i++){ mts[i] = new MyThread(); } for (int j = 0; j < mts.length; j++) { mts[j].start(); } } }
上面程序创建了多个线程,分别输出单例对象的hashcode,输出结果为所有单例对象的hashcode值都一样。
因此在多线程并发下这样的实现是无法保证实例实例唯一的,甚至可以说这样的失效是完全错误的。
要保证线程安全,我们就得需要使用同步锁机制 (synchronized关键字)
出现非线程安全问题,是由于多个线程可以同时进入getInstance()方法,那么只需要对该方法进行synchronized的锁同步即可:
public class MySingleton { private static MySingleton instance = null; private MySingleton(){} //静态方法若使用synchronized修饰后,那么该方法一定具有同步效果. public synchronized static MySingleton getInstance() { try { if(instance != null){//懒汉式 }else{ //创建实例之前可能会有一些准备性的耗时工作 Thread.sleep(300); instance = new MySingleton(); } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
从执行结果上来看,问题已经解决了,但是这种实现方式的运行效率会很低。
有效的缩小同步范围可以在保证并发安全的前提下提高并发效率。
使用同步块可以更精准的控制需要同步的代码片段:
synchronized(同步监视器对象){
需要同步运行的代码片段...
}
public class MySingleton { private static MySingleton instance = null; private MySingleton(){} //public synchronized static MySingleton getInstance() { public static MySingleton getInstance() { try { synchronized (MySingleton.class) { if(instance != null){//懒汉式 }else{ //创建实例之前可能会有一些准备性的耗时工作 Thread.sleep(300); instance = new MySingleton(); } } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
同步监视器对象可以是任何的java对象。
静态方法若使用synchronized修饰后,那么该方法一定具有同步效果.
public class Thread_syncDemo3 { public static void main(String[] args) { Thread t1 = new Thread(){ public void run(){ Foo.dosome(); } }; Thread t2 = new Thread(){ public void run(){ Foo.dosome(); } }; t1.start(); t2.start(); } } class Foo{ public synchronized static void dosome(){ try { Thread t = Thread.currentThread(); System.out.println(t.getName()+":正在执行dosome方法..."); Thread.sleep(5000); System.out.println(t.getName()+":执行dosome方法完毕"); } catch (Exception e) { } } }
线程池