java复习二
想复习一下Java并发,看 Java 特种兵 并发部分 感觉讲的不错,写一下读书笔记
两个使用线程的方法
- 实现Runnable接口的多线程
package myList;
public class threadtest {
public static void main(String[] args) {
ThreadTests ds1 = new ThreadTests("阿三");
ThreadTests ds2 = new ThreadTests("李四");
Thread t1 = new Thread(ds1);
Thread t2 = new Thread(ds2);
t1.start();
t2.start();
}
}
class ThreadTests implements Runnable {
private String name;
public ThreadTests(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + ": " + i);
}
}
}
//李四: 0
阿三: 0
李四: 1
阿三: 1
阿三: 2
李四: 2
李四: 3
阿三: 3
阿三: 4
李四: 4
2.扩展Thread类实现的多线程
package myList;
public class TestThread extends Thread {
public TestThread(String name) {
super(name);
}
public void run() {
for(int i = 0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+" :"+i);
}
}
public static void main(String[] args) {
Thread t1 = new TestThread("阿三");
Thread t2 = new TestThread("李四");
t1.start();
t2.start();
}
}
//李四 :0
阿三 :0
李四 :1
阿三 :1
阿三 :2
李四 :2
阿三 :3
李四 :3
阿三 :4
李四 :4
new Thread()操作没有完成对线程的创建,只有当调用start()方法时才会真正在系统中存在一个线程,Thread本身对应的实例只是JVM内一个普通Java对象,是一个线程操作的外壳,而不是真正的线程。
线程状态
1.NEW 状态
表示线程还没有启动,只是创建了一个Java外壳,要注意的是调用了start()方法不表示状态就立即改变。
2.RUNNABLE 状态
程序运行状态,任务也有可能处于等待状态
3.BLOCKED 状态
BLOCKED称为阻塞状态,原因通常是它在等待一个锁
4.WAITING 状态
通常是指一个线程拥有对象锁后进入到相应的代码区域后,调用相应的锁对象的wait()方法操作后产生的一种结果,与BLOCKED状态比较,BOLOCKED状态是虚拟机认为程序还不能进入某个区域,发生wait()操作的条件就是要进入临界区,只是还有一些其他配合的资源没有准备充分,所有产生了等待。
notify()方法是唤醒一个处于WAITING状态的线程
5.TIMED_WAITING 状态
Thread.sleep()就可以进入TIMED_WAITING状态
6.TERMINATED 状态
线程结束时的状态,run()方法执行完成
wait()与notify()
它们都使用synchronized,它们的实现是基于对象,
interrupt()
interrupt()操作在线程处于BLOCKEN, RUNNING状态没有作用,只对WAITING, TIME_WAITING状态的线程有用,让它们产生异常
还可以通过isInterrupt()判断线程是否已经被调用过中断方法
stop()与interrupt()
interrupt()是相对缓和的处理方式,stop()对处于RUNNING状态的任务会导致任务直接抛出java.lang.ThreadDeath的Error,会有一定的风险。
线程优先级
Java提供了1-10个优先级,理论上数字越大优先级越高
可以通过setPriority()来设置优先级
在JVM中还有一种特殊的后台线程,可以通过setDaemon(boolean)设置是否为后台线程,它通常优先级很低,JVM的GC线程就是后台线程,后台线程有一种很重要的的特征,如果JVM进程中活着的线程只有后台线程,就要结束整个进程。
线程合并(Join)
package myList;
class ThreadTesterA implements Runnable {
private int counter;
@Override
public void run() {
while (counter <= 10) {
System.out.print("Counter = " + counter + " ");
counter++;
}
System.out.println();
}
}
class ThreadTesterB implements Runnable {
private int i;
@Override
public void run() {
while (i <= 10) {
System.out.print("i = " + i + " ");
i++;
}
System.out.println();
}
}
public class ThreadTester {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new ThreadTesterA());
Thread t2 = new Thread(new ThreadTesterB());
t1.start();
t1.join(); // wait t1 to be finished
t2.start();
t2.join(); // in this program, this may be removed
}
}
//i = 0 Counter = 0 Counter = 1 i = 1 Counter = 2 i = 2 Counter = 3 i = 3 Counter = 4 Counter = 5 Counter = 6 Counter = 7 Counter = 8 Counter = 9 Counter = 10
i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10
可以实现同步的作用
线程安全
一致性问题一般出现在多核机器上
package myList;
public class ThreadTest extends Thread{
private boolean ready;
private int number;
public void run(){
while(!ready){
number++;
}
System.out.println(ready);
}
public void readyOn(){
this.ready = true;
}
public static void main(String [] args){
ThreadTest readThread = new ThreadTest();
readThread.start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
readThread.readyOn();
System.out.println(readThread.ready);
}
}
代码可能会不能停止,因为不同变量的修改不需要立即写回到主存,而线程读取也不需要每一次去读取数据。
解决方法:1.在循环中添加一个Thread.yield(),操作就可以让线程让步CPU,进而很可能到主存中读取最新的ready的值。2.在循环中加一条System.out语句,或者将ready变量增加一个volatile修饰符也可以达到退出的目的。
ThreadLocal
ThreadLocal可以放一个线程级别的变量,但它本身可以被多个线程共享使用,而又达到线程安全的目的,且绝对线程安全。
public final static ThreadLocal<String> RESOURCE = new ThreadLocal<String>();
RESOURCE代表一个可以存放String类型的ThreadLocal对象,任何一个线程可以并发访问这个变量,对它进行写入,读取操作,都是线程安全的
原子性和锁
synchronized
synchronized通过在对象上加锁后进入临界区达到临界区串行访问的目的
1.普通方法前面加synchronized
synchronized public void test() {}
锁住了类的对象
2. 静态方法前面加synchronized
synchronized public static void test() {}
锁住了当前类
3. 代码块加锁
synchronized(object){
//代码
}
锁住的不是代码而是object对象