1.线程的基本状态
2.线程的基本操作
3. volatile与java内存模型
4.线程组
5.守护线程(Daemon)
6.线程优先级
7.线程安全与synchronized
8.隐蔽错误
1.线程的基本状态
线程的生命周期
2.线程的基本操作
新建线程
Thread tl=new Thread(){
@override
public void run(){
System.out.println("Hel1o,I am t1");
};
t1.start();
t1.start();
终止线程
Thread.stop() //立即终止线程所有活动
stop()方法在结束线程时,会直接终止线程,并立即释放这个线程所持有的锁,可能引起数据不一致,强烈建议不使用!!
//正确停止线程的方法
public static class ChangeObjectThread extends Thread {
volatile boolean stopme = false; //标记位,记录是否要停止线程
public void stopMe(){
stopme = true;
}
@Override
public void run() {
while (true) {
if (stopme){ //在合适的地方停止线程,避免数据不一致
System.out.println("exit by stop me");
break;
}
synchronized (u) {
int v = (int) (System.currentTimeMillis() / 1000);
u.setId(v);
//Oh, do sth. else
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
u.setName(String.valueOf(v));
}
Thread.yield();
}
}
}
线程中断
public void Thread.interrupt() //中断线程,仅发送通知,设置标记位,等待合适时机线程中断
public boolean Thread.isInterrupted() //判断是否被中断,判断标记位
public static boolean Thread.interrupted()//判断是否被中断,并清除当前中断状态
Thread tl=new Thread(){
@Override
public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){ //需要设立中断处理方法,否则无法响应中断
System.out.println("Interruted!");
break;
)
Thread.yield();
}
public class InterruputSleepThread {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000); //由于中断而抛出异常会清除中断标记
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep"); //sleep被中断后,重置标记位继续运行,保证数据一致性和完整性
//设置中断状态
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
};
t1.start();
Thread.sleep(2000);
t1.interrupt();
}
}
等待(wait)和通知(notify)
public final void wait() throws InterruptedException;
public final native void notify();
object.wait()和object.notify()方法必须包含在synchronized关键字中,因为要获取对象的监视器(monitor对象)
public class SimpleWN {
final static Object object = new Object();
public static class T1 extends Thread{
public void run()
{
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T1 start! ");
try {
System.out.println(System.currentTimeMillis()+":T1 wait for object ");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+":T1 end!");
}
}
}
public static class T2 extends Thread{
public void run()
{
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
object.notify();
System.out.println(System.currentTimeMillis()+":T2 end!");
try {
Thread.sleep(2000); //此处sleep()2秒,T1此时仍然需要等待2秒才会继续执行
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) {
Thread t1 = new T1() ;
Thread t2 = new T2() ;
t1.start();
t2.start();
//输出:
//1565249714094:T1 start!
//1565249714094:T1 wait for object
//1565249714094:T2 start! notify one thread
//1565249714094:T2 end!
//1565249716094:T1 end!
}
}
wait()和sleep()的区别:
1.wait必须在同步条件内使用,sleep无此要求
2.wait释放锁,sleep不会释放
3.wait为Object内的实例方法,sleep为Thread的静态方法
4.wait需要调用对象的notify方法唤醒,sleep知道时间结束或者被interrupt打断
挂起(suspend)和继续执行(resume)
废弃方法,不作介绍!!有需求可使用wait和notify
等待线程结束(join)和谦让yield
public final void join() throws InterruptedException //一直阻塞线程直至结束
public final synchronized vold join(long millis)throws InterruptedException //给出最大等待时间
join的机制实际为在方法调用线程循环wait,如下join()核心代码:
while(isAlive()){
wait(0);
}
public static native void yield();
yield()方法表示当前线程让出cpu计算资源,但还是会参与cpu资源的争夺,一般用于防止低优先级线程占用太多cpu资源
3. volatile与java内存模型
volatile关键字可以保证一定的原子性,但是不能替代锁,不能保证100%线程安全
volatile可以保证可见性和有序性,充当内存栅栏,禁止指令重拍
4.线程组
当一个系统中线程较多且功能分配比较明确是可以使用线程组
public class ThreadGroupName implements Runnable {
public static void main(String[] args) {
ThreadGroup tg = new ThreadGroup("PrintGroup");
Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");
t1.start();
t2.start();
System.out.println(tg.activeCount()); //2
tg.list(); //java.lang.ThreadGroup[name=PrintGroup,maxpri=10]
// Thread[T1,5,PrintGroup]
// Thread[T2,5,PrintGroup]
}
@Override
public void run() {
String groupAndName = Thread.currentThread().getThreadGroup().getName()
+ "-" + Thread.currentThread().getName();
while (true) {
System.out.println("I am " + groupAndName);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
activeCount()获取线程组中的活动线程数
list()则打印线程组中的所有线程信息
5.守护线程(Daemon)
守护线程在后台执行,如垃圾回收线程,与之对应的为用户线程,当守护线程要守护的对象不存在了,则守护线程自动退出
当一个java应用中只有守护线程时,java虚拟机自动退出
public class DaemonDemo {
public static class DaemonT extends Thread{
public void run(){
while(true){
System.out.println("I am alive");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t=new DaemonT();
t.setDaemon(true); //此方法必须在start方法前
t.start();
Thread.sleep(2000);
}
}
上述代码中 t被设置为守护线程,当主线程退出时,守护线程也会退出;若未设置为守护线程,主线程退出后,t线程将依旧打印.
6.线程优先级
线程可以设置优先级,优先级高的线程获取到cpu资源的概率会更大(但不是一定会比低优先级的线程先获取到cpu资源)
public class PriorityDemo {
public static class HightPriority extends Thread{
static int count=0;
public void run(){
while(true){
synchronized(PriorityDemo.class){ //此处锁住该类是为了让产生一次资源竞争,使优先级差异表现更为明显
count++;
if(count>10000000){
System.out.println("HightPriority is complete");
break;
}
}
}
}
}
public static class LowPriority extends Thread{
static int count=0;
public void run(){
while(true){
synchronized(PriorityDemo.class){
count++;
if(count>10000000){
System.out.println("LowPriority is complete");
break;
}
}
}
}
}
/**
* HightPriority先完成的次数多,但是 不保证
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Thread high=new HightPriority();
LowPriority low=new LowPriority();
high.setPriority(Thread.MAX_PRIORITY); //此方法可以设置优先级 0-10 数字
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
}
}
试验多次HightPriority总是能比LowPriority快,但是不保证一定是这样.
7.线程安全与synchronized
synchronized为重量级同步锁,实现线程间的同步,sychronize锁住的对象、方法和代码块,每一次只能有一个线程进行访问,从而保证了线程的安全
用法:
指定锁对象: synchronized(object){.....}
指定实例方法 : public synchronized void println()
指定静态方法: public static synchronized void println()
public class BadAccountingSync2 implements Runnable{
static int i=0;
public static synchronized void increase(){ //此处如果不适用static则为线程不安全,最终i值必定小于20000
i++;
}
@Override
public void run() {
for(int j=0;j<10000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new BadAccountingSync2());
Thread t2=new Thread(new BadAccountingSync2());
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i); //输出20000
}
}
8.隐蔽错误
数值溢出
int v1=1073741827;
int v2=1431655768;
System.out,println("vl="+v1); //1073741827
System.out.println("v2="+v2); //1431655768
int ave=(vl+v2)/2;
System.out,println("ave="+ave); //-894784850 数值溢出,两int数值相加超过int最大值时溢出
并发不安全的arraylist
public class ArrayListMultiThread {
static ArrayList al = new ArrayList(10);
public static class AddThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
al.add(i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new AddThread());
Thread t2=new Thread(new AddThread());
t1.start();
t2.start();
t1.join();t2.join();
System.out.println(al.size());
}
}
以上程序可能会有三个结果
1.正常结束,打印值为2000000 (运气好)
2.正常结束,打印值小于2000000 (一般情况下会出现这种并发不安全的现象)
3.抛出异常,arrayindexoutofbound(扩容过程中内部一致性遭破坏,导致数组越界)
如何避免此问题?
使用线程安全的vector代替arraylist
并发不安全的hashmap
public class JDK8HashMapMultiThread {
static Map map = new HashMap();
static int nullNum = 0; //记录空值数
public static class AddThread implements Runnable {
int start = 0;
public AddThread(int start) {
this.start = start;
}
@Override
public void run() {
for (int i = start; i < 10000; i += 2) {
map.put(Integer.toString(i), Integer.toString(i));
}
}
}
public static void test() throws Exception {
Thread t1 = new Thread(new JDK8HashMapMultiThread.AddThread(0));
Thread t2 = new Thread(new JDK8HashMapMultiThread.AddThread(1));
t1.start();
t2.start();
t1.join();
t2.join();
}
public static void main(String[] args) throws Exception {
test();
for (int i = 0; i < 10000; i++) {
if (map.get("" + i) == null) {
nullNum += 1;
}
}
System.out.println(10000 - nullNum); //hashmap中存有的entry数
System.out.println(map.size()); //hashmap中size属性的值
}
}
打印值为: 6573
9715
打印结果不一定,但可以看出线程一定不安全,原因在于:
1.put时,两个entry的key hash值一致,同时进入插入代码,导致只有一个值插入成功
2.插入成功后size数值 进行size++,非原子操作,导致并发不安全
注意:
另网上有其他博文说会产生resize闭链问题,此问题只存在jdk1.8版本以前,1.8版本已优化过hashmap,故不存在闭链问题
想要避免并发问题可以使用concurrenthashmap
错误的加锁
public class BadLockOnInteger implements Runnable{
public static Integer i=0; //integer为不变类型,对其加锁无效
static BadLockOnInteger instance=new BadLockOnInteger();
@Override
public void run() {
for(int j=0;j<1000000;j++){
synchronized(i){
// synchronized(instance){ //改为对instance加锁即线程安全了
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i); //打印值小于2000000
}
}
注意:
对于不变对象如基本类型 Integer String 等加锁,相当于每次都锁不同的对象,从而导致并发问题产生.