線程相關
1.線程組
/**
* 線程組:將類似功能的線程放入同一組內,便於管理
* @author wsz
* @date 2017年11月27日
*/
class Print1 implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
while(true) {
System.out.println(name);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Print2 implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
while(true) {
System.out.println(name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadGroupTest implements Runnable{
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("ThreadGroup");//線程組及命名
// Thread tg1 = new Thread(group,new ThreadGroupTest(),"tg1");
// Thread tg2 = new Thread(group,new ThreadGroupTest(),"tg2");
Thread tg1 = new Thread(group, new Print1(), "print1");
Thread tg2 = new Thread(group, new Print2(), "print2");
tg1.start();
tg2.start();
System.out.println(group.activeCount());//估值活動線程數目
group.list();//線程組中線程信息
}
@Override
public void run() {
String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
while(true) {
System.out.println(name);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.守護線程
/**
* 守護線程:一般在後臺完成系統性的服務,比如垃圾回收線程、JIT線程。
* 工作線程:完成程序業務的線程。當期線程結束,那麼守護線程也將停止工作。因此當程序中只存在守護線程時,java虛擬機就會退出。
* @author wsz
* @date 2017年11月27日
*/
class Daemon1 implements Runnable{
@Override
public void run() {
while(true) {
System.out.println("alive");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Daemon1());
//設置爲守護線程,且必須在線程start之前設置,否則出現java.lang.IllegalThreadStateException異常,
//且線程將一直打印“alive”,因爲當前程序只有main主線程,當main休眠2秒後程序將退出。
//如果t不是守護線程,在main結束後,t線程仍將一直打印不會結束。
t.setDaemon(true);
t.start();
// t.setDaemon(true); //出現異常,且不會終止,將一直打印。
t.sleep(2000);
}
}
3.線程優先級
優先級別高的線程有更高的機會獲取足夠的資源運行。使用1-10表示線程優先級,數字越大線程優先級越高。Thread類中定義了三個靜態標量。 /**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
/**
* 線程優先級:數字1-10,數字越大級別越高
* @author wsz
* @date 2017年11月28日
*/
public class PriorityThread {
public final static Object object = new Object();
class HighThread implements Runnable{
int count = 0;
@Override
public void run() {
while(true) {
synchronized (object) {
count++;
if(count > 200000) {
System.out.println("HighThread is ok");
break;
}
}
}
}
}
class LowThread implements Runnable{
int count = 0;
@Override
public void run() {
while(true) {
synchronized (object) {
count++;
if(count > 200000) {
System.out.println("LowThread is ok");
break;
}
}
}
}
}
public static void main(String[] args) {
Thread high = new Thread(new PriorityThread().new HighThread());
Thread low = new Thread(new PriorityThread().new LowThread());
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
high.start();
low.start();
}
}
4.synchronized
public class AddThread implements Runnable{
static AddThread instance = new AddThread();
static volatile int count = 0;
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(count);//count值一般總是小於2個線程累加之和的。即使使用了volatile
}
@Override
public void run() {
for(int i = 0; i < 10000 ;i ++) {
count++;
}
}
}
爲了實現線程之間的同步可以使用synchronized關鍵字對同步的代碼塊進行加鎖。當線程進入代碼塊之前需要先獲得對應的鎖資源,即可保證線程間的安全性,並確保線程的原子性、可見性(只能由一個線程運行同步塊內的程序,不會衝突)、有序性(同步塊內程序只能由一個線程運行)。
- 指定加鎖對象:對給定對象加鎖,進入同步塊之前必須先獲得該對象的鎖資源(注意鎖對象的可變性,比如Integer不可變,不能作爲加鎖對象)
- 直接作用於實例方法:相當於對當前實例加鎖,進入同步塊之前必須先獲得當前實例的鎖資源
- 直接作用於靜態方法:相當於對當前類加鎖,進入同步塊之前必須先獲得當前類的鎖資源
public class AddThread implements Runnable{
static AddThread instance = new AddThread();//同一個對象
static volatile int count = 0;
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(count);//count值計算正確爲20000
}
@Override
public void run() {
synchronized (instance) {
for(int i = 0; i < 10000 ;i ++) {
count++;
}
}
}
}
作用於實例對象:public class AddThread implements Runnable{
static AddThread instance = new AddThread();
static volatile int count = 0;
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(count);//count值計算正確爲20000
}
@Override
public void run() {
// synchronized (instance) {
// for(int i = 0; i < 10000 ;i ++) {
// count++;
// }
// }
for(int i = 0 ;i < 10000 ; i++) {
increase();
}
}
public synchronized void increase() {//作用於實例方法,
count++;
}
}
如果想新建不同的實例對象並保證線程安全,可以把同步方法設置爲static修飾,這樣就是對當前類進行加鎖,而不是當前實例。public class AddThread implements Runnable{
static AddThread instance = new AddThread();
static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
//此時指向同一個實例對象,保證線程關注到同一個對象鎖資源上。
// Thread t1 = new Thread(instance);
// Thread t2 = new Thread(instance);
//此時作用於靜態方法,可以實例化不同對象
Thread t1 = new Thread(new AddThread());
Thread t2 = new Thread(new AddThread());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);//count值計算正確爲20000
}
@Override
public void run() {
// synchronized (instance) {
// for(int i = 0; i < 10000 ;i ++) {
// count++;
// }
// }
for(int i = 0 ;i < 10000 ; i++) {
increase();
}
}
public static synchronized void increase() {//作用於實例方法,
count++;
}
}
5.集合與線程安全問題
5.1ArrayList
public class ArrayListTest {
static ArrayList<Integer> arry = new ArrayList<Integer>();
class AddList implements Runnable{
@Override
public void run() {
for(int i= 0; i< 10000 ; i++)
arry.add(i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread a1 = new Thread(new ArrayListTest().new AddList());
Thread a2 = new Thread(new ArrayListTest().new AddList());
a1.start();
a2.start();
a1.join();
a2.join();
System.out.println(arry.size());//size值將出現小於20000的情況
}
}
運行上面程序ArrayList將添加數字,可能出現情況:- 正常結果20000
- 拋出下面的異常信息:在ArrayList擴容時,內部一致性被破壞,但是沒有鎖保護,另一個線程訪問到了不一致的內部狀態,出現越界問題。
- 打印結果小於正常值20000:此時保存容器大小的變量被多線程不正常訪問,即讀寫不一致,導致賦值出現爲題。
- 解決方法:可以使用線程安全的Vector代替。
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 10
at java.util.ArrayList.add(ArrayList.java:459)
at arrayList.ArrayListTest$AddList.run(ArrayListTest.java:14)
at java.lang.Thread.run(Thread.java:748)
10002
5.2HashMap
public class HashMapTest {
static HashMap<String,Object> map = new HashMap<String,Object>();
class AddHashMap implements Runnable{
@Override
public void run() {
for(int i= 0; i< 100 ; i++)
map.put(String.valueOf(i), i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new HashMapTest().new AddHashMap());
Thread t2 = new Thread(new HashMapTest().new AddHashMap());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(map.size());//size值將出現小於200的情況
}
}
最後github代碼地址:https://github.com/BeHappyWsz/Thread.git