Java 线程初步
http://chuhanzhi.com/?p=66 点击链接看整理后的文章
创建线程两种方法:
1. 继承 Thread 类,并覆盖其中 run 方法如下:
package thread;
public class FirstThread extends Thread {// 直接继承 Thread 类
@Override
public void run() {
while (true) {
System.out.println(1);
System.out.println(this.getName());
}
}
public static void main(String[] args) {
FirstThread th = new FirstThread();
th.setName("th1");
th.start();
Thread th2=new Thread(new SecondThread(),"th2");
Thread th3 =new Thread(th2,"th3");// 因为 Thread 本身实现了 Runnable 接口所以 Thread 可以作为 Thread 构造函数的参数当然很少会这么做
th2.start();
th3.start();
}
}
2. 实现 Rannable ,这样就可以使线程可以再继承其它类接口如下:
package thread;
public class SecondThread implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("2");
System.out.println(Thread.currentThread().getName());
}
}
}
停止线程:
1. 当线程正常结束时可以终止,在 FirstThread 中 run 方法改为既可以在执行完后自动退出
public void run() {
System.out.println(1);
System.out.println(this.getName());
}
2. 使用标记使线程停止
package thread;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FlagStopThread extends Thread {
public volatile boolean exit = false;
@Override
public void run() {
while (!exit) {
System.out.println("111111111");// 注意这里面不要是能产生阻塞的操作,如阻塞队列的 put 操作,这样的话可能会不往下执行,从而不会对 exit 进行及时的检查,而不能停止线程的执行
// 而当有这种情况发生时,使用中断来结束线程是一个比较好的选择
}
// BlockingQueue queue = null;// 不可取消的任务在退出前保存中断
// boolean interrupt=false;
// while(true){
// try {
// queue.take();
// } catch (InterruptedException ex) {
// interrupt=true;
// }finally{
// if(interrupt){
// Thread.currentThread().interrupt();
// }
// }
// }
}
public static void main(String[] args) throws InterruptedException {
FlagStopThread stopThread = new FlagStopThread();
stopThread.start();
sleep(1000);// 主线程休眠
stopThread.exit = true;
System.out.println("stop.........");
//stopThread.join();
}
}
3. 使用 interrupt 停止
package thread;
public class InterruptStopThread extends Thread {
// 使用 interrupt 中断线程
/**
* interrupt 设置线程的中断状态
* isInterrupted 判断线程是否中断
* interrupted 静态方法 判断当前线程中断状态,并且还会将该线程的中断状态置为 false ,因此连续两次调用该方法,
* 第二次返回值为 false (当然这连续的两次调用之间该线程又被中断的情况除外)
*/
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("11111111111" + this.isInterrupted());
sleep(1000);// 在 sleep 时调用了 interrupt 后状态标志位就变为 true , sleep 等阻塞方法会不断检查线程的状态,
// 当阻塞时会抛出异常进入 catch 块中,同时将标志重置为 false (这是在 sleep 等阻塞方法中完成的)
} catch (InterruptedException ex) {
/**
* 抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。
*/
System.out.println(this.isInterrupted()+" 处理中断请求 ");
this.interrupt();// 再次将中断标志设为 true 使 while 循环结束,该线程终止//不用这样的话try直接包住while块
} finally {
}
}
}
public static void main(String[] args) throws InterruptedException {
InterruptStopThread interruptStopThread = new InterruptStopThread();
interruptStopThread.start();
sleep(1000);
interruptStopThread.interrupt();
System.out.println(interruptStopThread.isInterrupted()+"5555");
}
}
4. 还有 stop 等不推荐的方法
不推荐就不深研究了
sleep join wait notify 方法
wait方法:
package thread;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SleepThread extends Thread {
@Override
public void run() {
try {
while (true) {
System.out.println("1111111111...");
sleep(1000);//sleep 有两个重载形式,另一个重载形式不仅可以设置毫秒还可以设置纳秒但大多数操作系统上的 java 虚拟机都无法精确到秒这时就会取近似的毫秒值
//suspend(); 挂起和唤醒线程,不建议使用
//resume();
}
} catch (InterruptedException ex) {// 在这里 run 方法无法使用 throws 因此只能使用 try{}catch(){}, 当在休眠过程中使用 interrupt 方法时会 sleep 会抛出一个 InterruptedException 异常
System.out.println("111");
}
}
public static void main(String[] args) {
SleepThread sleepThread = new SleepThread();
sleepThread.start();
}
}
join方法
package thread;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JoinThread extends Thread {
/**
* 如果简单数据类型声明为 volatile ,对它们的操作就会变成原子级别的。但这有一定的限制。要对表达式加上 synchronized 关键字
*
* Java 的内存模型分为主存储区和工作存储区。
* 主存储区保存了 Java 中所有的实例。也就是说,在我们使用 new 来建立一个对象后,这个对象及它内部的方法、变量等都保存在这一区域,
* 在 Jointhread 类中的 n 就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈,
* 在这个区域里保存了在 run 方法以及 run 方法所调用的方法中定义的变量,也就是方法变量。
* 在线程要修改主存储区中的变量时,并不是直接修改这些变量,而是将它们先复制到当前线程的工作存储区,
* 在修改完后,再将这个变量值覆盖主存储区的相应的变量值。由此可见 n++ 并非原子操作
*/
public volatile static int n = 0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
n = n + 1;
try {
sleep(1000);// 这里 n=n+1 不是原子级别的。为了使结果更随机,延迟一段时间
} catch (InterruptedException ex) {
Logger.getLogger(JoinThread.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(Thread.currentThread().getName() + " n=" + n);
}
}
public static void main(String[] args) {
JoinThread[] joinThreads = new JoinThread[3];
for (int i = 0; i < joinThreads.length; i++) {
joinThreads[i] = new JoinThread();
}
for (int i = 0; i < joinThreads.length; i++) {
joinThreads[i].setName("thread" + i);
joinThreads[i].start();
// try {
// joinThreads[i].join();
// } catch (InterruptedException ex) {
// Logger.getLogger(JoinThread.class.getName()).log(Level.SEVERE, null, ex);
// }
}
System.out.println("n=" + n);
}
}
synchronized
package thread;
//http://stackoverflow.com/questions/437620/java-synchronized-methods-lock-on-object-or-class/437627#437627
public class Synchronized1Thread implements Runnable {
public String methodName;
// public synchronized void method1() {
// while (true) {
// System.out.println("1111111" + Thread.currentThread().getName() + " " + methodName);
//
// }
// }
//method1 可以等同于写成以下方法
public void method1() {
synchronized (this) {
while (true) {
System.out.println("1111111" + Thread.currentThread().getName() + " " + methodName);
}
}
}
public synchronized void method2() {
while (true) {
System.out.println("2222222");
}
}
public static synchronized void method3() {
while (true) {
System.out.println("3333333" + Thread.currentThread().getName());
}
}
//method3 等同于以下方法
// public static void method3() throws ClassNotFoundException {
// synchronized (Class.forName("Synchronized1Thread")) {
// while (true) {
// System.out.println("3333333" + Thread.currentThread().getName());
// }
// }
// }
public static synchronized void method4() {
while (true) {
System.out.println("4444444");
}
}
@Override
public void run() {
try {
this.getClass().getMethod(methodName).invoke(this);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
/*
* 一个 synchronized 方法执行时需要一个锁(监视器)
* 一个对象只有且只能提供一个锁
* 对于静态方法有 synchronized 时它的锁是所在类的 Class 对象 所以静态方法和非静态方法不管在什么情况下都是可以并发执行的,
* 因为不同方法上加锁的对象是不同的(非静态方法的锁是类的一个实例对象,而静态方法则是所在类的 Class 对象)
*/
Synchronized1Thread syn1 = new Synchronized1Thread();
Synchronized1Thread syn2 = new Synchronized1Thread();
syn1.methodName = "method1";
syn2.methodName = "method4";
new Thread(syn1).start();
Thread.sleep(100);// 加一个延迟使上面那个线程有足够时间运行到 run 方法中去
new Thread(syn2).start();//1 1 (此时加锁的是不同的实例对象), 1 2 , 1 3 可同时进入,但 3 3 , 3 4 不可同时进入( 1 1 指的是不同进程运行 method1 和 method1 能否并发执行)
// Synchronized1Thread syn1 = new Synchronized1Thread();
// syn1.methodName = "method1";
// new Thread(syn1).start();
// Thread.sleep(100);
// syn1.methodName = "method3";
//
// new Thread(syn1).start();;//1 2 , 1 1 不可同时进入, 1 3 可以说明静态方法和非静态方法无影响
}
}
wait方法
package thread;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WaitThread {
/*
* wait 使方法临时阻塞跟 sleep join 等相似,但它将线程置于睡眠状态之后还会积极的等待条件发生改变。
* 在一个 notify 或 notifyAll 后会唤醒等待的线程重新可以运行。
* 在使用 notify 时要注意如果有多个线程是因调用一个对象的 wait 方法而阻塞的那么 notify 会随机选择一个被阻塞的线程解除阻塞,这时可用 notifyAll
* 调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。
* 而调用任意对象的 notify() 方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)
* 要释放锁的前提当然是要有锁因此有 wait 的地方一定有 synchronized ,只有 synchronized 方法或块中当前线程才占有锁,才有锁可以释放
*/
public static void main(String[] args) {
NotifyThread notifyThread = new NotifyThread();
System.out.println("result is coming soon...");
notifyThread.start();
// try {// 如果加上这句可能就是先执行了 notify 再执行下面的 wait 会阻塞,因此实际运行 wait 或 notify 时应根据情况进行条件检查
// Thread.sleep(1000);
// } catch (InterruptedException ex) {
// Logger.getLogger(WaitThread.class.getName()).log(Level.SEVERE, null, ex);
// }
synchronized (notifyThread) {
try {
notifyThread.wait();
//notifyThread.wait();// 如果加上这个当被原来那个 wait 的阻塞被解除后又运行这个 wait 后又阻塞了,因为没有别的线程可以为其解除阻塞所以会一直阻塞下去
} catch (InterruptedException ex) {
Logger.getLogger(WaitThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.out.println("result is:" + notifyThread.a);
}
}
class NotifyThread extends Thread {
public Integer a = 0;
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
a = a + i;
}
notify();
}
}
}
生产者消费者
package thread;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CustomerProducterThread {
public static void main(String[] args) {
Storage storage = new Storage();
Customer c1 = new Customer(storage);
c1.setName("c1");
Customer c2 = new Customer(storage);
c2.setName("c2");
Customer c3 = new Customer(storage);
c3.setName("c3");
Customer c4 = new Customer(storage);
c4.setName("c4");
Producter p1 = new Producter(storage);
p1.setName("p1");
Producter p2 = new Producter(storage);
p2.setName("p2");
Producter p3 = new Producter(storage);
p3.setName("p3");
Producter p4 = new Producter(storage);
p4.setName("p4");
c1.start();
c2.start();
p1.start();
p2.start();
p3.start();
p4.start();
c3.start();
c4.start();
}
}
class Storage {// 仓库
public static final int[] lock = new int[0];
public static final Integer max = 100;
private Integer sto = 0;
public void put(int i, String name) {// 生产者放
synchronized (lock) {
if ((this.sto + i) > max) {
System.out.println(name + " 仓库已满 ............" + i);
try {
lock.wait();
put(i,name);// 等 wait 结束后继续调用 put 方法来放置刚才生产的 i 个产品 , 即生产的产品放不进时不能丢,等解锁后再放
} catch (InterruptedException ex) {
Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
this.sto = this.sto + i;
System.out.println(name + " 生产了 " + i + " 个产品,现在库存量 " + this.sto);
lock.notifyAll();
}
}
}
public void get(int i, String name) {// 消费者取
synchronized (lock) {
if (this.sto - i < 0) {
System.out.println(name + " 仓库余额不足 ............" + i);
try {
lock.wait();
get(i,name);
} catch (InterruptedException ex) {
Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
this.sto = this.sto - i;
System.out.println(name + " 消费了 " + i + " 个产品,现在库存量 " + this.sto);
lock.notifyAll();
}
}
}
}
class Customer extends Thread {// 消费者
public Storage sto;
public Customer(Storage sto) {
this.sto = sto;
}
@Override
public void run() {
while (true) {
sto.get(new Random().nextInt(10) + 1, Thread.currentThread().getName());
}
}
}
class Producter extends Thread {// 生产者
public Storage sto;
public Producter(Storage sto) {
this.sto = sto;
}
@Override
public void run() {
while (true) {
sto.put(new Random().nextInt(10) + 1, Thread.currentThread().getName());
}
}
}
Dead Lock简单实例
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LeftRightDeadLockThread {
private Object left = new Object();
private Object right = new Object();
public void leftRight() {
synchronized (left) {
synchronized (right) {
System.out.println("leftRight");
}
}
}
public void rightLeft() {
synchronized (right) {
synchronized (left) {
System.out.println("rightLeft");
}
}
}
/*
* A- 锁住 left- 尝试锁住 right-- 永远等待
* B- 锁住 right- 尝试锁住 left-- 永远等待
*/
public static void main(String[] args) {
final LeftRightDeadLockThread lRDLT = new LeftRightDeadLockThread();
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(new Runnable() {
@Override
public void run() {
while (true) {
lRDLT.leftRight();
}
}
});
service.submit(new Runnable() {
@Override
public void run() {
while (true) {
lRDLT.rightLeft();
}
}
});
service.shutdown();
}
}
ThreadLocal:
//ThreadLocal的get方法
// public T get() {
// Thread t = Thread.currentThread();
// ThreadLocalMap map = getMap(t);//getMap(t)方法内部就是return t.threadLocals; 可见每个线程都有一个ThreadLocalMap的threadLocals属性
// if (map != null) {
// ThreadLocalMap.Entry e = map.getEntry(this);//如果该map不为空的话以该ThreadLocal的实例对象为key取出值并返回
// if (e != null)
// return (T)e.value;
// }
// return setInitialValue();//如果map为空或不为空但以此ThreadLocal的实例对象为key在map中取值为空时就调用此方法
// }
//setInitialValue()方法
//private T setInitialValue() {
// T value = initialValue();//initialValue()方法默认是返回空的,但是它是一个protected方法是为了子类重写准备的,因此如果多个线程要不相互干扰的使用同一个变量的时候,就可以重写这个方法然后new一个新的引用将那个变量的初值赋值给这个引用的对象就可以了
// Thread t = Thread.currentThread();
// ThreadLocalMap map = getMap(t);
// if (map != null)
// map.set(this, value);
// else
// createMap(t, value);
// return value;
// }
//set方法
//public void set(T value) {
// Thread t = Thread.currentThread();
// ThreadLocalMap map = getMap(t);
// if (map != null)
// map.set(this, value);
// else
// createMap(t, value);
// }
/**
* 综上可以看到ThreadLocal set 和 get的使用过程:
* 从set说起:
* 1.取得执行当前代码的线程
* 2.从取得的线程中取其ThreadLocalMap 的对象属性
* 3.以ThreadLocal为key往上面取得的map中放值
* 下面是get方法:
* 1.取得执行当前代码的线程
* 2.从取得的线程中取其ThreadLocalMap 的对象属性
* 3.map不为空以此ThreadLocalMap为键取值,如果取得值不为空则返回这个值。
* 如果为空或map本身为空则需返回initialValue方法中的值
* 上面的总结可以看出,ThreadLocal set()时可以将一个需要每个线程都分别存放的对象放到正在运行该方法的线程的ThreadLocalMap中
* get的时候再从正在运行的线程的ThreadLocalMap中取得这个对象,因此每个线程都能从自己的ThreadLocalMap中取得自己保存的值
*
* ThreadLocal适应的场合应该是每个线程对应一个实例的访问,用它的作用话应该是:可以使代码变得更“优雅”一些而避免了参数传递的麻烦,比如说在一个线程中
* 多个方法都需要一个变量而且是共享的,那么这个变量就可以放到ThreadLoca中,这看起来跟session有点像
*/
package thread;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ThreadLocalThread {//没有好例子暂时用这个吧
//
// ThreadLocal threadLocal = new ThreadLocal() {
//
// protected synchronized Object initialValue() {
// return new Integer(0);
// }
// };
public static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session =new ThreadLocal();
public static Session currentSession() throws HibernateException {//这样就能保证在每个线程中取得的Session是每个线程自己的那一个
Session s = (Session) session.get();
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null) {
s.close();
}
session.set(null);
}
}