多线程的使用(基础)
一、新启线程的方式
1、
public class NewThread {
/*扩展自Thread类*/
private static class UseThread extends Thread{
@Override
public void run() {
super.run();
//do my work
System.out.println("I am extended Thread.");
}
}
/*实现Runnable接口*/
private static class UseRunnable implements Runnable{
@Override
public void run() {
//do my work
System.out.println("I am implements Runnable.");
}
}
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<String>{
@Override
public String call() throws Exception {
//do my work
System.out.println("I am implements Callable.");
return "CallResult";
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseThread useThread = new UseThread();
useThread.start();
UseRunnable useRunnable = new UseRunnable();
new Thread(useRunnable).start();
UseCallable useCallable = new UseCallable();
FutureTask<String> futureTask //用FutureTask包装Callable
= new FutureTask<>(useCallable);
new Thread(futureTask).start();//交给Thread去运行
System.out.println("Get UseCallable result = "+futureTask.get());
}
}
future使用:
public class UseFuture {
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!结果为: "+sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();
FutureTask<Integer> futureTask //用FutureTask包装Callable
= new FutureTask<>(useCallable);
new Thread(futureTask).start();//交给Thread去运行
Random r = new Random();
Thread.sleep(1000);
if(r.nextBoolean()) {//用随机的方式决定是获得结果还是终止任务
System.out.println("Get UseCallable result = "+futureTask.get());
}else {
System.out.println("中断计算。 ");
futureTask.cancel(true);
}
}
}
2、守护线程:Thread.setDaemon(true);
二、线程中断
1、
private static class UseRunnable implements Runnable{
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {//改为true,线程不会中断;改为Thread.interrupted(),标志位被复位
System.out.println(Thread.currentThread().getName()
+ " I am implements Runnable.");
}
System.out.println(Thread.currentThread().getName()
+" interrupt flag is "+Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
UseRunnable useRunnable = new UseRunnable();
Thread endThread = new Thread(useRunnable,"endThread");
endThread.start();
Thread.sleep(20);
//中断
endThread.interrupt();
}
2、
interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态
isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态
阻塞方法中抛出InterruptedException异常后,如果需要继续中断,需要手动再中断一次
三、
Java 内存模型中的可见性、原子性和有序性:
1、可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。**也就是一个线程修改的结果。另一个线程马上就能看到。
2、原子是世界上的最小单位,具有不可分割性。**比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。在 Java 中 synchronized 和在 lock、unlock 中操作保证原子性。
3、Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
Volatile只保证内存可见性,不保证操作的原子性
4、volatile的使用
public class UseVola {
private volatile int age = 100000;
private static class TestThread extends Thread{
private UseVola synTest;
public TestThread(UseVola synTest,String name) {
super(name);
this.synTest = synTest;
}
@Override
public void run() {
for(int i=0;i<100000;i++) {
synTest.test();
}
System.out.println(Thread.currentThread().getName()
+" age = "+synTest.getAge());
}
}
public void test() {
age++;
}
public int getAge() {
return age;
}
public static void main(String[] args) throws InterruptedException {
UseVola synTest = new UseVola();
Thread endThread = new TestThread(synTest,"endThread");
endThread.start();
SleepTools.ms(5);
System.out.println(Thread.currentThread().getName()
+" age = "+synTest.getAge());
}
}
5、演示violate无法提供操作的原子性
public class VolatileUnsafe {
private static class VolatileVar implements Runnable {
private volatile int a = 0;
@Override
public void run() {
a = a + 1;
System.out.println(Thread.currentThread().getName() + ":----" + a);
SleepTools.ms(100);
a = a + 1;
System.out.println(Thread.currentThread().getName() + ":----" + a);
}
}
public static void main(String[] args) {
VolatileVar v = new VolatileVar();
Thread t1 = new Thread(v);
Thread t2 = new Thread(v);
Thread t3 = new Thread(v);
Thread t4 = new Thread(v);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
6、synchronized关键字的作用
public class SynTest {
private int age = 100000;//初始100000
private static class TestThread extends Thread{
private SynTest synTest;
public TestThread(SynTest synTest,String name) {
super(name);
this.synTest = synTest;
}
@Override
public void run() {
for(int i=0;i<100000;i++) {//递增100000
synTest.test();
}
System.out.println(Thread.currentThread().getName()
+" age = "+synTest.getAge());
}
}
public synchronized void test() {
age++;
}
public void test2() {
synchronized(this){
age--;
}
}
public int getAge() {
return age;
}
public static void main(String[] args) throws InterruptedException {
SynTest synTest = new SynTest();
Thread endThread = new TestThread(synTest,"endThread");
endThread.start();
for(int i=0;i<100000;i++) {//递减100000
synTest.test2();
}
System.out.println(Thread.currentThread().getName()
+" age = "+synTest.getAge());
}
}
7、 调用yield() 、sleep()、join()、wait()、notify()
yield() 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了,不释放锁
join()的主要作用就是同步**,它可以使得线程之间的并行执行变为串行执行
sleep()使调用该方法的线程进入停滞状态,所以执行sleep()的线程在指定的时间内
肯定不会被执行,不释放锁
wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized 关键字使用,调用前持有所,后会释放锁
Notify 唤醒某一个wait()线程,(随机唤醒),会释放锁
notifyAll 唤醒所有
演示demo:
join():
public class UseJoin {
static class JumpQueue implements Runnable {
private Thread thread;
public JumpQueue(Thread thread) {
this.thread = thread;
}
public void run() {
try {
thread.join();//调用传入线程的join方法,必须等这个方法返回后,当前线程才能继续执行
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + " terminate.");
}
}
public static void main(String[] args) throws Exception {
Thread previous = Thread.currentThread();//现在previous是主线程
for (int i = 0; i < 10; i++) {
// 每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
Thread thread =
new Thread(new JumpQueue(previous), String.valueOf(i));
System.out.println(previous.getName()+" jump a queue the thread:"
+thread.getName());
thread.start();
//传入当前线程
previous = thread;
}
SleepTools.second(2);//让主线程休眠2秒
System.out.println(Thread.currentThread().getName() + " terminate.");
}
}
wait/notify/notifyAll
public class Express {
public final static String CITY = "ShangHai";
private int km;/*快递运输里程数*/
private String site;/*快递到达地点*/
public Express() {
}
public Express(int km, String site) {
this.km = km;
this.site = site;
}
/* 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/
public synchronized void changeKm(){
this.km = 101;
notifyAll();
}
/* 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
public synchronized void changeSite(){
this.site = "BeiJing";
notifyAll();
}
public synchronized void waitKm(){
while(this.km<=100){//公里数小于100不做处理
try {
wait();
System.out.println("Check Km thread["+Thread.currentThread().getId()
+"] is be notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the Km is "+this.km+",I will change db");
}
public synchronized void waitSite(){
while(this.site.equals(CITY)){//快递到达目的地
try {
wait();
System.out.println("Check Site thread["+Thread.currentThread().getId()
+"] is be notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the site is "+this.site+",I will call user");
}
}
public class TestWN {
private static Express express = new Express(0,Express.CITY);
/*检查里程数变化的线程,不满足条件,线程一直等待*/
private static class CheckKm extends Thread{
@Override
public void run() {
express.waitKm();
}
}
/*检查地点变化的线程,不满足条件,线程一直等待*/
private static class CheckSite extends Thread{
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<3;i++){
new CheckSite().start();
}
for(int i=0;i<3;i++){
new CheckKm().start();
}
Thread.sleep(1000);
//express.changeKm();//快递地点变化
express.changeSite();
}
}
8、ThreadLocal的使用
使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
public class UseThreadLocal {
/*声明一个ThreadLocal类型的static变量,所有线程共享,并初始化为1*/
static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
/**
* 运行3个线程
*/
public void StartThreadArray(){
Thread[] runs = new Thread[3];
for(int i=0;i<runs.length;i++){
runs[i]=new Thread(new TestThread(i));
}
for(int i=0;i<runs.length;i++){
runs[i].start();
}
}
/**
*类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
*/
public static class TestThread implements Runnable{
int id;
public TestThread(int id){
this.id = id;
}
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
Integer s = threadLocal.get();//获得变量的值
s = s+id;
threadLocal.set(s);//将值写回变量
System.out.println(Thread.currentThread().getName()+':'+threadLocal.get());
}
}
public static void main(String[] args){
UseThreadLocal test = new UseThreadLocal();
test.StartThreadArray();
}
}