目录
线程停止
使用一个标志位终止变量,当flag=false,则线程停止
public class TestStop implements Runnable{
//1.设置一个标志位
boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("Thread run"+i++);
}
}
//2.设置一个stop方法
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
if (i==900){
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程休眠
-
sleep()指定当前线程阻塞的毫秒数
-
sleep存在异常InterruptedException
-
sleep时间到达后线程进入就绪状态
-
sleep可模仿网络延时,倒计时等
-
每一个对象都有一个锁,sleep不会释放锁
线程礼让
-
让当前正在执行的线程暂停,但不阻塞
-
将线程从运行状态转为就绪状态
-
让CPU重新调度,礼让不一定成功,看CPU心情
public class YieldDemo {
public static void main(String[] args) {
YieldTest y1 = new YieldTest();
new Thread(y1,"a").start();
new Thread(y1,"b").start();
}
}
class YieldTest implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
合并线程
待次线程执行完,再执行其他线程,其他线程阻塞
public class JoinDemo implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
JoinDemo j1 = new JoinDemo();
Thread thread=new Thread(j1);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
if (i==20){
thread.join();
}
System.out.println(i);
}
}
}
线程优先级
使用setPriority()和getPriority(),先设置优先级,再启动,优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,要看CPU的调度
守护线程daemon
-
线程分为用户线程和守护线程
-
虚拟机必须保证用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
public class TestDeamon {
public static void main(String[] args) {
God god = new God();
Yous you = new Yous();
Thread thread = new Thread(god);
thread.setDaemon(true);//正常是FALSE,表示用户线程
thread.start();//上帝守护线程启动
new Thread(you).start();//用户线程启动
}
}
//上帝类
class God implements Runnable{
public void run() {
while (true){
System.out.println("上帝守护着你");
}
}
}
//你
class Yous implements Runnable{
public void run() {
for (int i = 0; i < 36000; i++) {
System.out.println("你每一天都开心的活着");
}
System.out.println("你生命结束了");
}
}
线程同步
并发:同一个对象被多个线程同时操作
形成条件:队列+锁
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制Synchronized ,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可.存在以下问题:
-
一个线程持有锁会导致其他所有需要此锁的线程挂起;
-
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
-
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置,引起性能问题.
同步方法
由于我们可以通过private关键字来保证数据对象只能被方法访问﹐所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法︰synchronized方和synchronized 块.
同步方法:public synchronized void method(int args)
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都须获得调用该方法的对象的锁才能执行﹐否则线程会阻塞,方法一旦执行﹐就独占该锁,到该方法返回才释放锁﹐后面被阻塞的线程才能获得这个锁﹐继续执行
缺陷:若将一个大的方法申明为synchronized将会影响效率
生产者消费者问题
public class TestPc {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Produce(synContainer).start();
new Customer(synContainer).start();
}
}
//生产者
class Produce extends Thread{
SynContainer container;
public Produce(SynContainer container){
this.container=container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//生产100次产品
//给容器添加产品
container.push(new Chicken(i));
System.out.println("生产者放入了产品编号为:"+i+"的产品");
}
}
}
//消费者
class Customer extends Thread{
//通过容器操作
SynContainer container;
//通过有参构造传入容器
public Customer(SynContainer container){
this.container=container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//一次线程消费100次
//调用容器,取产品
Chicken chicken = container.pop();
System.out.println("消费者拿走了产品编号为:"+chicken.id+"的产品");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果满了需要等待消费者消费
if (count==10){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
this.notify();
}
//消费者拿走商品
public synchronized Chicken pop(){
if (count<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
this.notify();
return chicken;
}
}
线程池
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建和销毁、实现重复利用。类似生活中的公共交通工具
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
- 便于线程管理
ExecutorService:真正的线程池接口
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
<T>Future<T>submit(Callable<T> task)
:执行任务,有返回值,一般用来执行Callable- void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new MyThtead());
executorService.execute(new MyThtead());
executorService.execute(new MyThtead());
executorService.execute(new MyThtead());
//结束线程
executorService.shutdown();
}
}
class MyThtead extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName());
}
}