多线程温习2. 多线程之间的同步
现在我们要启动2个线程,第一个线程打印“gaoxiaowei”中的每一个字符,然后换行; 第二个线程打印“zhangwenbin”的每一个字符然后换行,代码如下:
-
public class TraditionalThreadSynchronized {
-
-
/**
-
* @param args
-
*/
-
public static void main(String[] args) {
-
new TraditionalThreadSynchronized().init();
-
}
-
-
private void init(){
-
final Outputer outputer = new Outputer();
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("gaoxiaowei");
-
}
-
-
}
-
}).start();
-
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("zhangwenbin");
-
}
-
-
}
-
}).start();
-
-
}
-
-
-
static class Outputer{
-
-
public void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
-
-
public void output2(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
public static void output3(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
}
-
}
我们预期的输出是:
gaoxiaowei
zhangwenbin
gaoxiaowei
gaoxiaowei
zhangwenbin
类似于这样,一个线程逐个打印完字符串中的每个字符后,第二个线程再开始打印第2个字符串中的每个字符。
可是,上述代码,打印出的字符串里包含如下的字符串:
gzhanaoxiaowei
gwenbin
gaoxiaowei
zhangwenbin
gaoxiaowei
zhangwenbin
zhangwengaoxibin
aowei
gazhangweoxnbin
iaowei
zhanggaoxiaoweiwenbin
这种是如何导致的呢?是因为这2个线程公用了一个电脑屏幕资源,我们的输出设备只有一个,但是抢占的线程有2个。当CPU时间片分 给线程1的时候它打印出了g,然后这是线程1挂起,CPU时间片轮到了线程2,这时线程2打印zhan到屏幕,接着又是线程1继续打印aoxiaowei剩下的字符。 这说明我们的2个线程并没有同步,而是不配合的抢占CPU资源,导致你输出你的,我输出我的,最终大家配合的不好,导致屏幕上的输出,谁也没得到甜处——完整的输出自己的字符串。
如何破解?需要加同步关键字synchronized,我们将这个关键字加到output函数上,代码如下:
-
public synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
这下就好了,输出达到了预期。synchronized表示2个线程执行同一个output对象的output函数时,要排序执行,也就是说第1个线程执行完了,第2个线程才能开始,synchronized是一把同步锁,只有第1个线程执行完output函数后,锁子才能释放,别的线程才能继续执行outputer.output函数。
这时我们把第二个线程的 outputer.output改为output3. 现在的代码如下:
-
public class TraditionalThreadSynchronized {
-
-
/**
-
* @param args
-
*/
-
public static void main(String[] args) {
-
new TraditionalThreadSynchronized().init();
-
}
-
-
private void init(){
-
final Outputer outputer = new Outputer();
-
final Outputer outputer2 = new Outputer();
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("gaoxiaowei");
-
}
-
-
}
-
}).start();
-
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer. output3("zhangwenbin");
-
}
-
-
}
-
}).start();
-
-
}
-
-
-
static class Outputer{
-
-
public synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
-
-
public void output2(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
public static void output3(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
}
试问,输出还能不错乱吗,能达到预期吗?
答案是不能达到预期,会错乱!因为当线程1执行output的时候,synchronized只对output上了锁,而output3并没有上锁。也就说,即使线程1没有执行完output函数时,没有释放锁的情况下,线程2照样可以执行output3函数来抢占屏幕资源。
如何破解?给output3也加锁synchronized关键字,实际上output与output3用的同步锁都为同一个锁:当前对象this。因此当线程1执行output的中途,this这个同步锁没有释放,那么线程2就被禁止执行output3,直到线程1执行完output函数并释放锁。
改正后的代码如下:
-
public class TraditionalThreadSynchronized {
-
-
/**
-
* @param args
-
*/
-
public static void main(String[] args) {
-
new TraditionalThreadSynchronized().init();
-
}
-
-
private void init(){
-
final Outputer outputer = new Outputer();
-
final Outputer outputer2 = new Outputer();
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("gaoxiaowei");
-
}
-
-
}
-
}).start();
-
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer. output3("zhangwenbin");
-
}
-
-
}
-
}).start();
-
-
}
-
-
-
static class Outputer{
-
-
public synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
-
-
public void output2(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
public synchronized void output3(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
}
-
}
接下来,把output函数改为static类型:
-
public static synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
这时线程1的output与线程2的output3会同步吗?
答案:不会。
因为静态函数output不专属于某一个对象,它是属于整个类的。因此output3的synchronized同步锁,锁不住output的执行。
如何破解?
方法1:把output3也变为static类型,
-
public static synchronized void output3(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
这时output和output3的synchronized锁就指向一个共同的锁了:类的字节码Outputer.class
方法2:现在output函数是static的类型,那么它的锁实质就是synchronized(Outputer.class); 那么,我们可以把output3的同步锁也改成字节码:
-
public void output3(String name){
-
synchronized(Outputer.class)
-
{
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
}
这时output与output3拥有了同一个锁,这下输出就正常了。总之,要想同步使用临界资源:屏幕。为了保证按顺序打印,就得为各个output函数上同一把锁,切记是同一把。最后的代码如下:
-
public class TraditionalThreadSynchronized {
-
-
/**
-
* @param args
-
*/
-
public static void main(String[] args) {
-
new TraditionalThreadSynchronized().init();
-
}
-
-
private void init(){
-
final Outputer outputer = new Outputer();
-
final Outputer outputer2 = new Outputer();
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("gaoxiaowei");
-
}
-
-
}
-
}).start();
-
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output3("zhangwenbin");
-
}
-
-
}
-
}).start();
-
-
}
-
-
-
static class Outputer{
-
-
public static synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
-
-
public void output2(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
public void output3(String name){
-
synchronized(Outputer.class)
-
{
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
}
-
}
思考:现在我们把线程2更改为outputer2.output2, 注意是outputer2对象的output2函数,.对象变了。我们为output2函数也加把锁synchronized, 现在的代码如下:
-
public class TraditionalThreadSynchronized {
-
-
/**
-
* @param args
-
*/
-
public static void main(String[] args) {
-
new TraditionalThreadSynchronized().init();
-
}
-
-
private void init(){
-
final Outputer outputer = new Outputer();
-
final Outputer outputer2 = new Outputer();
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer.output("gaoxiaowei");
-
}
-
-
}
-
}).start();
-
-
new Thread(new Runnable(){
-
@Override
-
public void run() {
-
while(true){
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
outputer2.output2("zhangwenbin");
-
}
-
-
}
-
}).start();
-
-
}
-
-
-
static class Outputer{
-
-
public static synchronized void output(String name){
-
int len = name.length();
-
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
-
}
-
-
public synchronized void output2(String name){
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
public void output3(String name){
-
synchronized(Outputer.class)
-
{
-
int len = name.length();
-
for(int i=0;i<len;i++){
-
System.out.print(name.charAt(i));
-
}
-
System.out.println();
-
}
-
-
}
-
}
输出能同步吗?
答案:不能,因为outputer.output用的锁是 类的字节码Outputer.class,而outputer2.output2函数的synchronized 指的是output2.this这个对象。它们俩不是同一把锁,因此最终还会对屏幕进行错乱输出,抢占资源时发生冲突。
文章来源: blog.csdn.net,作者:冉航--小虾米,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/gaoxiaoweiandy/article/details/106122756
- 点赞
- 收藏
- 关注作者
评论(0)