文章目录
一、线程、进程、多线程
同时执行多个事情
进程是系统分配的,数量受限(程序运行起来就变成进程了)
进程中有多个线程(数量上不受限),按照时间轮转发进行切换
sleep(): 强迫一个线程睡眠N毫秒。
yield: 线程礼让,让出当前资源,大家重新竞争
isAlive(): 判断一个线程是否存活。
join(): 给其他线程加塞,自己插队执行,直到结束
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束) ,垃圾回收,主线程,日志等都是主线程。;用户线程结束,守护线程结束(虚拟机不用等待守护线程结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
notifyAll(): 通知其他所有等待该竞争资源的线程
setPriority(): 设置一个线程的优先级。只是提高了该县城的调度概率
suspend() 使线程进入阻塞态,必须resume()方法被调用,才能使线程重新进入可执行状态
volatile :共享资源的安全
二、main主线程、gc垃圾回收线程
- 都是守护线程
三、继承thread方法实现多线程(本质也是实现Runnable)
public class TestThread_01 extends Thread {
@Override
public void run() {
for(int i = 1; i<6; i++){
System.out.println("我在执行线程!");
}
}
public static void main(String[] args){
TestThread_01 t1 = new TestThread_01();
t1.start();
for(int i = 1; i<100; i++){
System.out.println("我是main方法,主线程");
}
}
}
- 案例(下载图片)
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread_02 extends Thread{
private String url; //下载地址
private String name; // 保存文件名
public TestThread_02 (String url, String name) {
this.url=url;
this.name= name;
}
@Override
public void run() {
new WebDownload().download(url, name);
}
public static void main(String[] args){
new TestThread_02("http://img3.imgtn.bdimg.com/it/u=988449024,2456755617&fm=15&gp=0.jpg", "1.jpg").start();
new TestThread_02("http://img3.imgtn.bdimg.com/it/u=988449024,2456755617&fm=15&gp=0.jpg", "2.jpg").start();
new TestThread_02("http://img3.imgtn.bdimg.com/it/u=988449024,2456755617&fm=15&gp=0.jpg", "3.jpg").start();
}
}
class WebDownload{
public void download(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、Runnable创建多线程(推荐,数据可共享的)
public class TestThread_03 implements Runnable {
@Override
public void run() {
for (int i = 0; i<6; i ++){
System.out.println("Runnable线程在跑");
}
}
public static void main(String[] args){
new Thread(new TestThread_03()).start();
System.out.println("主线程");
}
}
五、callable实现多线程
可以监视线程执行的结果
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
private String url; //下载地址
private String name; // 保存文件名
public TestCallable (String url, String name) {
this.url=url;
this.name= name;
}
@Override
public Boolean call() throws Exception {
new WebDownload().download(url, name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("http://a3.att.hudong.com/68/61/300000839764127060614318218_950.jpg", "1.jpg");
TestCallable t2 = new TestCallable("http://a3.att.hudong.com/68/61/300000839764127060614318218_950.jpg", "2.jpg");
TestCallable t3 = new TestCallable("http://a3.att.hudong.com/68/61/300000839764127060614318218_950.jpg", "3.jpg");
// 创建执行服务
ExecutorService es = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> result1 = es.submit(t1);
Future<Boolean> result2 = es.submit(t2);
Future<Boolean> result3 = es.submit(t3);
// 获取执行结果
Boolean r1 = result1.get();
Boolean r2 = result2.get();
Boolean r3 = result3.get();
es.shutdown();
}
}
class WebDownloads{
public void download(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
六、静态代理
public class StaticProxy {
public static void main(String[] args){
CompnanyProxy compnanyProxy = new CompnanyProxy(new You());
compnanyProxy.getmarry();
}
}
interface Marry{
void getmarry();
}
class You implements Marry{
@Override
public void getmarry() {
System.out.println("今天可以结婚了");
}
}
class CompnanyProxy implements Marry{
private Marry target;
public CompnanyProxy(Marry target){
this.target = target;
}
@Override
public void getmarry() {
before();
this.target.getmarry();
after();
}
private void after() {
System.out.println("生孩子");
}
private void before() {
System.out.println("布置新房");
}
}
七、建议使用标志位,正常停止线程,不推荐使用stop/destory等方法
1 ) 使用标志位
2)使用stop()方法,但该方法就像关掉电脑电源一样,可能会发生预料不到的问题
3)使用中断interrupt()
public class Thread {
// 中断当前线程
public void interrupt();
// 判断当前线程是否被中断
public boolen isInterrupt();
// 清除当前线程的中断状态,并返回之前的值
public static boolen interrupted();
}
八、线程同步(队列和锁)
1.共享数据不安全的演示
1.1买票
/**
* 共享数据不安全的演示
*/
public class UnsafeBuyTicket {
public static void main(String[] args){
BuyTicket station = new BuyTicket();
new Thread(station, "黄牛").start();
new Thread(station, "别人").start();
new Thread(station, "我").start();
}
}
class BuyTicket implements Runnable {
private int num = 10;
boolean flag = true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if(num<=0) {
flag = false;
return;
}
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+ "拿到"+ num --);
}
}
1.2 取钱
public class UnsafeBank {
public static void main(String[] args){
Account account = new Account("结婚基金", 100);
Drawing you = new Drawing(account, 50, "我");
Drawing wife = new Drawing(account, 80, "老婆");
you.start();
wife.start();
}
}
class Account{
String name; // 卡名
int money; // 余额
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
// 银行模拟取款
class Drawing extends Thread{
Account account;
// 去了多少钱
int drawingMoney;
// 现在手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
this.nowMoney = nowMoney;
}
@Override
public void run() {
if(account.money-drawingMoney<0){
System.out.println(this.getName() + "余额不足,取不到");
return;
}
try {
this.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 余额
account.money = account.money - drawingMoney;
// 你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里共有:" + nowMoney);
}
}
1.3 线程不安全的集合
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args){
List<String> list = new ArrayList<>();
for (int i=0; i<10000; i++) {
new Thread(() ->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
线程安全的集合:
Vector:就比Arraylist多了个同步化机制(线程安全)。
Hashtable:就比Hashmap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector。
除了早期的集合:后期出现的集合都是线程不安全的;Collections工具类中提供了相应的包装方法把它们包装成线程安全的集合
List<E> synArrayList = Collections.synchronizedList(new ArrayList<E>());
Set<E> synHashSet = Collections.synchronizedSet(new HashSet<E>());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
...
JUC并发编程中的CopyOnWriteArrayList和CopyOnWriteArraySet
它们是加了写锁的ArrayList和ArraySet,锁住的是整个对象,但读操作可以并发执行
除此之外还有ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等,至于为什么没有ConcurrentArrayList,原因是无法设计一个通用的而且可以规避ArrayList的并发瓶颈的线程安全的集合类,只能锁住整个list,这用Collections里的包装类就能办到
2.安全锁演示synchronized
synchronized(谁变锁谁)同步会降低执行效率
2.1 买票(synchronized)
/**
* 共享数据不安全的演示
*/
public class UnsafeBuyTicket {
public static void main(String[] args){
BuyTicket station = new BuyTicket();
new Thread(station, "黄牛").start();
new Thread(station, "别人").start();
new Thread(station, "我").start();
}
}
class BuyTicket implements Runnable {
private int num = 10;
boolean flag = true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
if(num<=0) {
flag = false;
return;
}
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+ "拿到"+ num --);
}
}
2.2 取钱演示
public class UnsafeBank {
public static void main(String[] args){
Account account = new Account("结婚基金", 100);
Drawing you = new Drawing(account, 50, "我");
Drawing wife = new Drawing(account, 80, "老婆");
you.start();
wife.start();
}
}
class Account{
String name; // 卡名
int money; // 余额
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
// 银行模拟取款
class Drawing extends Thread{
Account account;
// 去了多少钱
int drawingMoney;
// 现在手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
this.nowMoney = nowMoney;
}
@Override
public void run() {
// 锁的是变化的量
synchronized (account){
if(account.money-drawingMoney<0){
System.out.println(this.getName() + "余额不足,取不到");
return;
}
// sleep 放大问题
try {
this.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 余额
account.money = account.money - drawingMoney;
// 你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里共有:" + nowMoney);
}
}
}
2.3 集合
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i=0; i<10000; i++) {
new Thread(() ->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
2.3 集合加锁
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i=0; i<10000; i++) {
new Thread(() ->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
九、死锁
- 多个线程互相持有对方所需要的资源,形成僵持
public class DeadLock {
public static void main(String[] args){
MakeUp g1 = new MakeUp(0, "灰姑娘");
MakeUp g2 = new MakeUp(1, "大富婆");
g1.start();
g2.start();
}
}
// 口红
class Lipstick{}
// 镜子
class Mirror{ }
// 化妆
class MakeUp extends Thread{
// 重要的资源只有一个
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 选择
String girlName; // 使用化妆品的人
MakeUp(int choice, String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆
private void makeup() throws InterruptedException {
if(choice==0){
synchronized (lipstick){
System.out.println(this.girlName + "获得口红锁");
sleep(1000);
}
synchronized (mirror){
System.out.println(this.girlName + "获得镜子");
}
}else{
synchronized (mirror){
System.out.println(this.girlName + "获得镜子");
sleep(2000);
}
synchronized (lipstick){
System.out.println(this.girlName + "获得口红锁");
}
}
}
}
1、互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。
十、lock(锁) jDK1.5新增的锁
ReentrantLock 实现了Lock,在实现现成的安全中比较常用,可以显示的添加、释放锁
import java.util.concurrent.locks.ReentrantLock;
// 测试lock锁
public class TestLock {
public static void main(String[] args){
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int num =18;
// 定义lock锁
private ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
reentrantLock.lock();
if(num>0) {
Thread.sleep(1000);
System.out.println(num--);
}else {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
}
十一、线程间的通信
生产者消费者(生产者和消费者共享同一个资源)
1.1 管程法
// 生产者消费者通信: 利用缓冲区解决(管程法)
// 生产者、消费者、产品、缓冲区
public class TestPC {
public static void main(String[] args){
SynContainer container = new SynContainer();
new Productor(container).start();
new Customer(container).start();
}
}
// 生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
container.push(new Chicken(i));
System.out.println("生产了第" +i+ "只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消费者
class Customer extends Thread{
SynContainer container;
public Customer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Chicken pop = null;
try {
pop = container.pop();
System.out.println("消费了第" +pop.name+ "只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 产品
class Chicken{
int name;
public Chicken(int name) {
this.name = name;
}
}
// 缓冲区
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count = 0;
// 生产者生产产品
public synchronized void push(Chicken chicken) throws InterruptedException {
// 如果容器满了就通知消费者进行消费
if(count==chickens.length){
// 容器满了,生产等待
this.wait();
}
chickens[count] = chicken;
count ++ ;
// 通知消费
this.notify();
}
// 消费者消费产品
public synchronized Chicken pop() throws InterruptedException {
// 判断能否消费
if(count==0){
// 等待生产者生产,消费等待
this.wait();
}
count -- ;
Chicken chicken = chickens[count];
// 吃完了通知生产者生产
this.notify();
return chicken;
}
}
1.2 信号灯法
十二、线程池
管理线程并发数量/提高线程使用效率、提高运行速度
1. ExecutorService 和 Excutors
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
// 创建连接池
ExecutorService es = Executors.newFixedThreadPool(3);
// 执行
es.execute(runnable);
es.execute(runnable);
es.execute(runnable);
es.execute(runnable);
es.execute(runnable);
// 关闭连接
es.shutdown();
}
}