线程

单线程、多线程与进程

线程是依附于进程而存在的,当程序在运行时,就是进程,进程占用内存空间,线程不占用内存空间,一个进程至少有一个线程,例如下载器的下载,设置只能同时执行一个下载任务就是单线程,同时下载多个就是多线程,多线程的优点在于能够同时并发多个任务,并且线程之间不会相互干扰,同时提高CPU的利用率,缺点是线程变多,进程占用内存也会变多,多线程需要协调和管理,
多线程对共享资源的访问会存在竞用问题,例如银行取钱,划卡同时在网上取钱,这样银行操作员的页面没有刷新,就会产生问题,不过现在为多线程提供了管理,划卡后会锁住余额,操作员执行后才能网上取钱,同时线程太多会导致复杂难控制,也会出现多bug的问题
一个main方法执行时也是多线程的,main方法与垃圾回收器同步执行,也就是主线程和垃圾回收器线程:

public class Xian {

@Override
protected void finalize() throws Throwable {// 垃圾回收器执行时会调用finalize方法
	System.out.println("------------");
}

public static void main(String[] args) {
	new Xian();
	new Xian();// 创建垃圾,让垃圾回收器执行
	System.gc();// 建议垃圾回收器工作
	System.out.println("ssssssssss");
}
}

线程的创建一

public class CJXian1 extends Thread {// 继承一个线程类

private String name;

CJXian1(String name) {
	this.name = name;
}

public static void main(String[] args) {
	CJXian1 cjx1 = new CJXian1("aa");
	CJXian1 cjx2 = new CJXian1("bb");
	cjx2.setDaemon(true);//守护线程,守护主线程,只要主线程结束,守护线程也就随之结束,守护线程运行,代表着主线程也在运行,此方法必须在start方法之前
	cjx1.start();// start方法默认调用run方法,并且一个对象只能start一次,不能再start一次,否则会出错
	cjx1.setPriority(10);// 给线程一个优先级,优先级范围1~10,5为普通,10最高
	cjx2.start();
	try {
		cjx2.join();//哪个线程调用此方法,哪个线程就优先执行完
		yield();//暂停当前线程
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	try {
		cjx1.join();
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

public void run() {
	for (int i = 0; i < 10; i++) {
		System.out.println(getName());// 线程的名字
		System.out.println(currentThread());// 显示正在运行的线程的信息:[线程的名字,线程的优先级,线程的组名]
		System.out.println(i + " " + name);
	}

}
}

线程的创建二

public class CJXian2 implements Runnable {// 实现一个Runnable接口

@Override
public void run() {
	for (int i = 0; i < 10; i++) {
		System.out.println(Thread.currentThread());// 因为是实现接口,所以必须用类名点方法才能调用线程类里的方法
	}
}

public static void main(String[] args) {
	CJXian2 cjx = new CJXian2();
	Thread cjx1 = new Thread(cjx);// 创建线程对象来放本类的对象
	Thread cjx2 = new Thread(cjx);

	cjx1.start();
	cjx2.start();
}
}

总之,创建线程,要么实现Runnable接口,要么继承Thread类

线程同步代码块

当多个线程共同使用一个对象的时候,线程就会不安全,数据出现重复,这时候就需要线程同步,或者说是线程排队

继承Thread同步

public class XCTongBu extends Thread {

int a = 0;
String str = "adc";
private String name;

XCTongBu(String name) {
	this.name = name;
}

@Override
public void run() {

	synchronized (str) {// 为线程提供一个锁,可以是属性或者对象,使得线程同步

		for (int i = 0; i < 10; i++) {
			System.out.println(currentThread() + " " + a);
			a++;
		}
	}
}

public static void main(String[] args) {
	XCTongBu tb1 = new XCTongBu("aa");
	// 两个线程共享一个对象
	Thread t1 = new Thread(tb1);
	Thread t2 = new Thread(tb1);
	t1.start();
	t2.start();
}
}

实现Runnable同步

public class CJXian2 implements Runnable {// 实现一个Runnable接口

int b = 0;
String str = "abc";

public void run() {
	synchronized (str) {

		for (int i = 0; i < 10; i++) {
			System.out.println(b + " " + Thread.currentThread());
			b++;

		}
	}
}

public static void main(String[] args) {
	CJXian2 cjx = new CJXian2();
	Thread cjx1 = new Thread(cjx);
	Thread cjx2 = new Thread(cjx);

	cjx1.start();
	cjx2.start();
}
}

同步方法

public class CJXian2 implements Runnable {// 实现一个Runnable接口

static int b = 0;
String str = "abc";

public void run() {
	s();// 调用被同步的方法
}

public static synchronized void s() {// 同步方法

	for (int i = 0; i < 10; i++) {
		System.out.println(b + " " + Thread.currentThread());
		b++;

	}
}

public static void main(String[] args) {
	CJXian2 cjx = new CJXian2();
	Thread cjx1 = new Thread(cjx);
	Thread cjx2 = new Thread(cjx);

	cjx1.start();
	cjx2.start();
}
}

线程同步的前提在于有多个线程,并且他们共用一个锁,弊端在于每个线程都要去判断锁,降低了程序的运行效率

线程通信

消费实例

public class TestDemo {

public static void main(String[] args) {
	
	Warehouse wh = new Warehouse(); // 创建共享数据仓库实例
	ProducerThread pt = new ProducerThread(wh); // 创建生产者线程实例
	CustomerThread ct1 = new CustomerThread(wh, "yakov"); // 创建消费者线程实例1
	CustomerThread ct2 = new CustomerThread(wh, "张三"); // 创建消费者线程实例2
	
	//启动线程
	pt.start();
	ct1.start();
	ct2.start();
}

}

仓库类

import java.util.ArrayList;
import java.util.List;

/**
 * 仓库类,作为生产者线程和消费者线程的共享数据
* @author yakov
*
*/
public class Warehouse {
// 仓库中的商品库存
private List<String> products = new ArrayList<String>();

/**
 * 生产者生产商品,添加商品到仓库
 * @param productName 商品名
 */
public void addProduct(String productName) {
	products.add(productName);
}

消费者

/**
 * 消费者消费商品,从仓库中获取商品
 * @return 获取的商品名
 */
public String getProduct() {
	// 判断仓库是否为空
	if(!products.isEmpty()) {
		// 移除仓库中最早的商品,即返回给消费者
		String productName = products.remove(0);
		return productName;
	}
	System.out.println("库存为空!");
	return "";
}

/**
 * 获取库存量
 * @return 库存量大小
 */
public int getSize() {
	return products.size();
}
}

 /**
* 消费者线程类
* @author yakov
*
*/
public class CustomerThread extends Thread {
// 持有共享数据
private Warehouse wh;
// 消费者姓名
private String cname;

public CustomerThread(Warehouse wh, String cname) {
	super();
	this.wh = wh;
	this.cname = cname;
}

@Override
public void run() {
	// 不断消费
	while(true) {
		// 共享数据同步块
		synchronized (wh) {
			if (wh.getSize() == 0) {
				try {
					System.out.println("库存为空,消费者" + cname + "进入等待状态。。。");
					// 通过调用共享数据的wait方法,让本线程进入等待状态,直到被唤醒。
					wh.wait();
					System.out.println("消费者" + cname + "结束等待。");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else if (wh.getSize() > 0) {
				// 从仓库中拿走1个商品
				String productName = wh.getProduct();
				System.out.println("消费者" + cname + "拿走了商品" + productName);
			}
		}
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
}

生产者

 import java.util.Random;

/**
* 生产者线程类
* @author yakov
*
*/
public class ProducerThread extends Thread {
// 持有共享数据
private Warehouse wh;

public ProducerThread(Warehouse wh) {
	super();
	this.wh = wh;
}

@Override
public void run() {
	// 不断的生产
	while(true) {
		// 随机生成一个商品名
		String productName = "p" + (new Random()).nextInt(1000);
		
		// 共享数据同步块
		synchronized (wh) {
			// 往仓库添加商品
			wh.addProduct(productName);
			System.out.println("生产者往仓库添加商品:" + productName);
			// 唤醒拥有wh,且处于”等待状态“的其它所有线程
			wh.notify();
 //				wh.notifyAll();
		}
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
}

线程通讯中用了3个object中的方法,用锁来调用,分别是wait(等待)、notify(唤醒一个)、notifyAll(唤醒全部),以及Thread中的sleep(睡)方法
wait和sleep的区别:wait要配合锁使用,wait状态是锁是开着的,sleep状态锁是关闭的,并且sleep内填的是毫秒数,为程序继续执行提供间隔时间,否则程序会飞速执行下去

线程死锁

线程死锁是不恰当地使用Synchronized来管理线程对特定对象的访问造成的,使得两个线程相互持有对方依赖的共享资源,造成的无限阻塞

例子:

package sisuo.com;

public class TestDemo {

public static void main(String[] args) {
	String a = "今天星期五。";
	String b = "明天还上班。";
	MyDeadLockThread1 t1 = new MyDeadLockThread1(a, b);
	MyDeadLockThread2 t2 = new MyDeadLockThread2(a, b);
	
	t1.start();
	t2.start();
	
}

}

线程一:

package sisuo.com;

/**
* 线程1
* @author yakov
*
*/
public class MyDeadLockThread1 extends Thread {
private String a;
private String b;

public MyDeadLockThread1(String a, String b) {
	super();
	this.a = a;
	this.b = b;
}

@Override
public void run() {
	System.out.println("线程1开始执行了。。。。");
	// 锁定a资源
	synchronized (a) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 在锁定了a资源后,又要锁定b资源,此时b资源又被另一线程占用,引发资源竞争,造成阻塞死锁
		synchronized (b) {
			System.out.println("资源b为:" + b);
		}
	}
	System.out.println("线程1执行完毕!");
}
}

线程二:

package sisuo.com;

/**
* 线程2
* @author yakov
*
*/
public class MyDeadLockThread2 extends Thread {
private String a;
private String b;

public MyDeadLockThread2(String a, String b) {
	super();
	this.a = a;
	this.b = b;
}

@Override
public void run() {
	System.out.println("线程2开始执行了。。。。");
	// 锁定b资源
	synchronized (b) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 在锁定了b资源后,又要锁定a资源,此时a资源又被另一线程占用,引发资源竞争,造成阻塞死锁
		synchronized (a) {
			System.out.println("资源a为:" + a);
		}
	}
	System.out.println("线程2执行完毕!");
}
}

线程死锁的解决办法:
尽量不要使用嵌套的Synchronized语句
让线程持有独立的资源
死锁要通过优良的设计来尽量避免死锁的情况发生

线程停止

package tingzhi.com;

import java.util.Date;

public class ThreadStop {// 类

static class MyTimer implements Runnable {// static 类
	boolean flag = true;

	public void run() {

		while (flag) { // ----------通过标识来停止
			System.out.println("进入while....");
			System.out.println(" " + new Date() + " ");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
			System.out.println("" + Thread.currentThread().getName() + "结束");

		}

	}

	public void stopRun() {

		flag = false;
	}

}

public static void main(String[] args) {

	MyTimer t = new MyTimer();
	Thread tt = new Thread(t);
	tt.setName("Timer");
	tt.start();
	for (int i = 0; i < 1000; i++) {

		System.out.println("" + i);

	}
	// try {
	// Thread.sleep(100);
	// } catch (InterruptedException e) {
	//
	// e.printStackTrace();
	// }
	t.stopRun();

}
}

线程状态

新建——>可运行——>运行——>(join、sleep、等待输入)阻塞——>可运行——>运行——>死亡
新建——>可运行——>运行——>(等待队列)wait(notify、notifyAll)——>同步锁池(取得对象锁)——>可运行——>运行——>死亡
新建——>可运行——>运行——>(同步锁池)Synchronized(取得对象锁)——>可运行——>运行——>死亡
新建——>可运行——>运行——>(死亡)main正常退出、结束或异常退出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值