进程
1.运行时(runtime)应用程序
2.进程之间的内存不是共享(独占)
3.进程间通信使用socket(套接字)
多线程
1.进程内并发执行的代码段
2.线程之间共享内存
3.每个运行着的线程对应一个stack
4.应用线程至少有一个线程(主线程)
1.Thread.yield()方法
让当前线程让出CPU的抢占权,瞬时的动作。
2.Thread.sleep()
让当前线程休眠指定毫秒数
释放CPU抢占权,和锁旗标没有关系。
3.Object: wait
让当前线程进入锁旗标的等待队列。释放CPU抢占权,还释放锁旗标的监控权。
字符在内存中存储都是unicode编码。
要注意:将对象传递给线程时,传递的只是对象的引用,而不是对象的拷贝
public class TestCase1 {
@Test
public void test4() throws InterruptedException {
Zhong zhong = new Zhong();
Thread t1 = new MyThread2(zhong, "中秋快乐");
Thread t2 = new MyThread2(zhong, "你就是我最美的期待");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("所有线程结束");
System.out.println(Thread.currentThread().getName() + ", zhong = " + zhong + ", desc = " + zhong.getDesc());
}
}
class MyThread2 extends Thread{
private Zhong zhong;
public MyThread2(Zhong zhong, String desc) {
this.zhong = zhong;
this.zhong.setDesc(desc);
}
public void run() {
System.out.println(Thread.currentThread().getName() + ", zhong = " + zhong + ", desc = " + zhong.getDesc());
}
}
class Zhong{
private String desc;
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
打印结果:
Thread-0, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待
Thread-1, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待
所有线程结束
main, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待
继承Thread并重写run方法来定义线程
* 第一种创建线程的方式
* 继承Thread并重写run方法来定义线程要执行的任务
public class Demo1 {
public static void main(String[] args) {
myThread1 t1 = new myThread1();
myThread2 t2 = new myThread2();
* 启动线程要指定start方法,而不是直接调用run方法,
* run方法时线程要执行的任务。
* 当线程的start方法被调用后, 线程进入runnable状态,
* 一旦获取cpu时间,run方法会自动调用。
t1.start();
t2.start();
}
}
* 这种创建线程的方式有两个不足:
* 1. 由于java是单继承,那么当继承了Thread后,就无法再继承其他类。
*
* 2.由于继承Thread后重写run方法规定了线程执行的任务,
* 这就导致线程与任务有一个必然的耦合关系,不利于线程的重用。
class myThread1 extends Thread{
public void run() {
for(int i=0; i<250; i++) {
System.out.print("*");
if(i>0 && i%50==0) {
System.out.println();
}
}
}
}
class myThread2 extends Thread{
public void run(){
for(int i=0; i<250; i++) {
System.out.print("-");
if(i%50==0) {
System.out.println();
}
}
}
}
实现Runnable接口并重写run方法,实例传入Thread中
* 第二种创建线程的方式
* 实现Runnable接口并重写run方法
public class Demo2 {
public static void main(String[] args) {
MyRunnable1 r1 = new MyRunnable1();
MyRunnable2 r2 = new MyRunnable2();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable{
public void run() {
for(int i=0; i<250; i++) {
System.out.print("*");
if(i%50==0) {
System.out.println();
}
}
}
}
class MyRunnable2 implements Runnable{
public void run() {
for(int i=0; i<250; i++) {
System.out.print("-");
if(i%50==0) {
System.out.println();
}
}
}
}
匿名内部类创建上面两种线程
* 使用匿名内部类来完成方式一与方式二的线程创建
public class Demo3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run() {
for(int i=1; i<251; i++) {
System.out.print("*");
if(i%50 == 0) {
System.out.println();
}
}
}
};
t1.start();
-------------方式二
new Thread(new Runnable() {
public void run() {
for(int i=1; i<250; i++) {
System.out.print("-");
if(i%50 == 0) {
System.out.println();
}
}
}
}).start();
}
}
获取运行当前方法的线程,Thread.currentThread()
* static Thread currentThread()
* 获取运行当前方法的线程
public class Demo4 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
System.out.println("运行main线程:" + main);
dosome();
}
public static void dosome() {
Thread t = Thread.currentThread();
System.out.println("运行dosome线程:"+t);
Thread t2 = new Thread() {
public void run() {
System.out.println("运行自建线程:"+Thread.currentThread());
}
};
t2.start();
}
}
打印:
运行main线程:Thread[main,5,main]
运行dosome线程:Thread[main,5,main]
运行自建线程:Thread[Thread-0,5,main]
获取线程相关信息的方法
* 获取线程相关信息的方法
public class Demo5 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
String mainName = main.getName();
long mainId = main.getId();
int priority = main.getPriority();
System.out.println("name:"+mainName);
System.out.println("id:"+mainId);
System.out.println("优先级:"+priority);
boolean isAlive = main.isAlive();
System.out.println("是否存活"+isAlive);
boolean isDaemon = main.isDaemon();
System.out.println("是否是守护线程:"+isDaemon);
boolean isInterrupted = main.isInterrupted();
System.out.println("是否中断:"+isInterrupted);
}
}
打印:
name:main
id:1
优先级:5
是否存活true
是否是守护线程:false
是否中断:false
* 线程优先级
* 线程的时间片分配完全听线程调度的,线程只能被动的被分配时间,对于线程调度的工作不能干预。
*
* 但是可以通过提高线程的优先级来达到尽可能干预的目的。
* 理论上,优先级越高的线程,获取CPU的时间片的次数就越多。
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
sleep,线程堵塞
import java.text.SimpleDateFormat;
import java.util.Date;
* static void sleep(long ms)
* 线程提供的静态方法sleep可以使运行该方法的线程进入堵塞
* 状态指定毫秒,超时后线程会自动回到RUNNABLE状态。
public class Demo7 {
public static void main(String[] args) {
while(true) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String time = sdf.format(new Date());
System.out.println(time);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
打印: 一秒钟打印一次当前时间
08:25:16
08:25:17
08:25:18
yield方法让出CPU时间
* yield方法
* Thread的静态方法yield:static void yield()
* 该方法用于使当前线程主动让出当次CPU时间片段回到RUNNABLE状态,等待分配时间片。
package archive;
public class cc {
public static void main(String[] args) {
Thread t1 = new MyThread("Thread-1");
Thread t2 = new MyThread("Thread-2");
t1.start();
t2.start();
}
}
class MyThread extends Thread{
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for(;;) {
System.out.println(name);
Thread.yield();
}
}
}
守护线程,所有前台线程结束,守护线程就会被结束
* 守护线程,又称为后台线程
* 当一个进程中的所有前台线程都结束时,进程就要结束,
* 若还有后台线程运行,那么后台线程会被强制结束。
public class Demo8 {
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for(int i=0; i<10; i++) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
while(true) {
System.out.println("hi");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
-------- 设置为后台线程,并且要在start前调用
t2.setDaemon(true);
t1.start();
t2.start();
}
}
运行结果:
t2 跟随 t1 结束而结束
* main方法是一个前台线程,GC是一个后台线程
join 堵塞线程,等待某个线程执行完毕,再继续执行
* void join()
* join方法可以使调用该方法的线程进入堵塞状态,直到该方法所属
* 线程完成工作才会解除调用该方法线程的堵塞状态。
* join方法一般用来完成多个线程之间的同步工作问题。
public class Demo9 {
static boolean isFinish = false;
public static void main(String[] args) {
Thread download = new Thread() {
public void run() {
for(int i=1; i<=6; i++) {
System.out.println("download:"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("download is successful");
isFinish = true;
}
};
Thread show = new Thread() {
public void run() {
System.out.println("showing");
-- 要先等待图片下载完毕
try {
download.join(); ---- 等待download线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!isFinish) {
throw new RuntimeException("show:图片显示异常");
}
System.out.println("show end");
}
};
download.start();
show.start();
}
}
打印:
showing
download:1%
download:2%
download:3%
download:4%
download:5%
download:6%
download is successful
show end
一个很明显join堵塞的例子
package archive;
public class cc {
public static void main(String[] args) {
Thread t1 = new MyThread("Thread-4", 4000);
Thread t2 = new MyThread("Thread-3", 3000);
Thread t3 = new MyThread("Thread-2", 2000);
Thread t4 = new MyThread("Thread-1", 1000);
t1.start();
t2.start();
t3.start();
t4.start();
try {
t1.join();
System.out.println("-----------");
t2.join();
System.out.println("-----------");
t3.join();
System.out.println("-----------");
t4.join();
}catch(Exception e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread{
private String name;
private int time;
public MyThread(String name, int time) {
this.name = name;
this.time = time;
}
public void run() {
System.out.println(name + "等待");
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "到了");
}
}
在t1.join()堵塞直到t1线程结束后才走下一句
执行结果:
Thread-3等待
Thread-1等待
Thread-2等待
Thread-4等待
Thread-1到了
Thread-2到了
Thread-3到了
Thread-4到了
-----------
-----------
-----------
synchronized,同步锁,线程排队执行
在方法上添加,是给对象加锁,如果是静态方法,静态方法所属类,就是给类加锁
* 多线程并发访问同一资源时,就会形成“抢”的现象,
* 由于线程切换时机不确定,可能导致执行代码顺序的混乱,严重时会导致系统瘫痪。
*
* 当一个方法被synchronized修饰后,该方法为同步方法,
* 即:多个线程不能同时进入方法内部执行。
* 对于成员方法而言,synchronized会在一个线程调用该方法将该方法所属对象加锁,
* 其他线程在执行该方法时由于执行方法的线程没有释放锁,所以只能在方法外堵塞,
* 直到持有方法锁的线程将方法执行完毕。
* 所以,解决多线程并发执行安全问题的办法就是将"抢"变为"排队"
public class Demo1 {
public static void main(String[] args) {
Bean bean = new Bean();
Thread t1 = new Thread() {
public void run() {
while(true) {
bean.OperateB();
}
}
};
Thread t2 = new Thread() {
public void run() {
while(true) {
bean.OperateB();
}
}
};
t1.start();
t2.start();
}
}
class Bean{
static int b = 20;
public synchronized void OperateB() {
b--;
if(b<=0) {
throw new RuntimeException("b is end!");
}else {
System.out.println(Thread.currentThread()+" b="+b);
}
}
}
同步块,块中的代码同一时间只能被一个线程执行
* 同步块
* 有效的缩小同步范围可以在保证并发安全的同时尽可能提高并发效率。
*
* 同步块可以要求多个线程对该块内的代码排队执行,但是前提条件是
* 同步监视器对象即(上锁的对象)要求多个线程看到的必须是同一个。
*
* synchronized(同步监视器对象){
* 需要同步的代码
* }
*
* 所谓同步执行即:多个线程必须排队执行
* 所谓异步执行即:多个线程可以同时执行
public class Demo2 {
public static void main(String[] args) {
Shop shop = new Shop();
Thread t1 = new Thread() {
public void run() {
shop.buy();
}
};
Thread t2 = new Thread() {
public void run() {
shop.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
public void buy() {
System.out.println("shop is begin");
try {
Thread.sleep(2000);
synchronized (this) {
System.out.println(Thread.currentThread() + "--hello");
Thread.sleep(2000);
}
}catch(Exception e) {
}
System.out.println("shop is end");
}
}
注意:
synchronized(this)
当中 this 指代的是 synchronized 所在的【对象】,
为 this 时本程序的执行的结果是:
shop is begin
shop is begin
Thread[Thread-1,5,main]--hello --- 锁住了
shop is end
Thread[Thread-0,5,main]--hello
shop is end
当 this 改为 new Object 时,
shop is begin
shop is begin
Thread[Thread-0,5,main]--hello --- 锁无效了
Thread[Thread-1,5,main]--hello
shop is end
shop is end
同步监视器对象即(上锁的对象)要求多个线程看到的必须是同一个。
类的静态方法加上 sysnchronized,那么类实例出来的对象执行该方法时也是具有同步锁的效果,因为该方法全局就一份,与对象无关
* 静态方法的同步锁
* 当一个静态方法被synchronized修饰后,那么该方法即为同步方法,
* 由于静态方法从属类,全局就一份,所以同步的静态方法一定具有同步锁效果,与对象无关。
public class Demo3 {
public static void main(String[] args) {
Foo f1 = new Foo();
Foo f2 = new Foo();
Thread t1 = new Thread() {
public void run() {
f1.dosome();
}
};
Thread t2 = new Thread() {
public void run() {
f2.dosome();
}
};
t1.start();
t2.start();
}
}
class Foo{
public synchronized static void dosome() {
try {
System.out.println("dosome");
Thread t = Thread.currentThread();
System.out.println(t.getName()+": dosome is running");
Thread.sleep(3000);
System.out.println(t.getName()+": dosome is end");
}catch(Exception e) {
e.printStackTrace();
}
}
}
打印:
dosome
Thread-0: dosome is running
Thread-0: dosome is end
dosome
Thread-1: dosome is running
Thread-1: dosome is end
互斥锁,,要十分注意锁的对象在多线程中看到的是否是同一个
* 互斥锁
* synchronized也叫互斥锁,即:
* 使用synchronized修饰多段代码,只要他们的同步监视器对象相同,
* 那么这几段代码间就是互斥关系,即:多个线程不能同时执行这些代码
public class Demo4 {
public static void main(String[] args) {
Foo1 foo = new Foo1();
Thread t1 = new Thread() {
public void run() {
foo.methA();
}
};
Thread t2 = new Thread() {
public void run() {
foo.methB();
}
};
t1.start();
t2.start();
}
}
class Foo1{
public synchronized void methA() {
try {
System.out.println("methA is begin");
Thread.sleep(4000);
System.out.println("methA is end");
}catch(Exception e) {
}
}
public synchronized void methB() {
try {
System.out.println("methB is begin");
Thread.sleep(4000);
System.out.println("methB is end");
}catch(Exception e) {
}
}
}
打印:
methA is begin ----- 尽管运行的不是同一个方法,但是锁的是同一个对象
methA is end
methB is begin
methB is end
** 注意:互斥锁修饰多段代码,同步锁修饰一段代码
ArrayList,HashSet ,HashMap 都不支持线程安全,要使用Collections内置的静态方法转换为线程安全的数据模型。
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Demo5 {
public static void main(String[] args) {
* ArrayList 不是线程安全的
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
System.out.println(list);
* 将给定集合转换为线程安全的集合
list = Collections.synchronizedList(list);
System.out.println(list);
* HashSet 不是线程安全的
Set<String> set = new HashSet<String>(list);
set.add("a");
set.add("b");
System.out.println(set);
* 将给定的 Set 集合转换为线程安全的
set = Collections.synchronizedSet(set);
System.out.println(set);
* HashMap 也不是线程安全的
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Language",98);
map.put("Math",90);
map.put("Englist",80);
System.out.println(map);
* 将 Map 转换为线程安全的
map = Collections.synchronizedMap(map);
System.out.println(map);
}
}
* API手册上有说明
* 就算是线程安全的集合那么其中对于元素的操作,如:add,remove等方法
* 都不予迭代器遍历做互斥,需要自行维护互斥关系。
线程池,重用线程,控制线程数量
* 线程池
* 线程池主要有两个作用:
* 1. 重用线程 ----- 而不是销毁后重建线程
* 2. 控制线程数量
* 当我们的应用需要创建大量线程或者发现线程会频繁的创建和销毁时
* 就应当考虑使用线程池来维护线程。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo6 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i=0; i<3; i++) {
Runnable runn = new Runnable() {
public void run() {
Thread t = Thread.currentThread();
try {
System.out.println(t.getName()+"线程开始");
Thread.sleep(3000);
System.out.println(t.getName()+"线程结束");
}catch(Exception e) {
e.printStackTrace();
}
}
};
threadPool.execute(runn);
System.out.println("指派了一个任务");
}
System.out.println("设置结束");
* 停止线程池有两种方式:
* 1. threadPool.shutdown(); ---- 分配的任务执行完毕就销毁所有线程
* 2. threadPool.shutdownNow(); ---- 立即结束所有线程
threadPool.shutdown();
}
}